> 文章列表 > Arrow开源项目Slice问题

Arrow开源项目Slice问题

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());

本节完~