Arrow开源项目Slice问题
Arrow开源项目Slice问题
背景
最近在做Arrow相关的工作,碰到一个slice问题,简单描述一下场景:
假设现在要对一个数组进行累加,输入:一个数组,输出一个值。
那如果做Slice呢?输入:一个切片数组,输出一个值。
遇到的问题是对Array做了Slice之后,输入竟然还是一个数组。
具体给社区提的pr里面讨论了相关问题,感兴趣的可以一起交流,地址如下:
https://github.com/apache/arrow/pull/35098
Batch与Array
下面是Batch做Slice的操作:可以看到这里对array做了Slice。
ExecBatch ExecBatch::Slice(int64_t offset, int64_t length) const {ExecBatch out = *this;for (auto& value : out.values) {if (value.is_scalar()) continue;value = value.array()->Slice(offset, length);}out.length = std::min(length, this->length - offset);return out
}
那么对于下面的代码可能跟我一样会觉得得到的答案就是对Slice的数组求和。
auto arraydata = *(batch[0].array());
ConsumeSumArray(arraydata.child_data[0]);
实际并不是,你会发现child_data并没有做Slice,与你期望的Slice不符。
问题剖析
对于Array,用户暴露接口操作,而底层是ArrayData,一个ArrayData里面有可以有child,被称之为多列数组,为何会有这种场景呢?
普通的数组:ArrayData,数据就是本身。
含有孩子的数组:ArrayData有多个,所以这里会衍生出多种上层接口,例如:StructArray。
在我们的实际场景中使用了StructArray,对于已经Slice的Batch,我们理想情况是这个StructArray已经被Slice了,可实际并不是,原因是对于ArrayData具有child_data的部分并没有做这一块逻辑。
在Arrow的官方代码里面有一段契约:
// The logical start point into the physical buffers (in values, not bytes).
// Note that, for child data, this must be *added* to the child data's own offset.
int64_t offset = 0;
这个offset指的是父类的,这里期望孩子的offset与父亲保持一致,在Slice接口中并没有对child做操作。
所以,要解决这个问题,官方不建议直接对ArrayData做接口性改动,同时也会破坏该契约,那么这里就有两种办法解决它:
方法1: 手动调用Slice,例如:
arraydata.child_data[0]->Slice(...);
方法2: 基于现有的ArrayDat构造出StructArray,随后调用field接口,该接口已支持Slice操作。
ConsumeSumArray(struct_array->field(0)->data());
本节完~