|
定义/术语
由于不同的项目使用不同的词语描述各种概念,所以这里有一个小小的术语表来帮助消除歧义。
- 数组:已知长度具有相同类型的值序列。
- 槽或数组槽:一些特定数据类型的数组中的单个逻辑值
- 连续的内存区域:给定长度的顺序虚拟地址空间。任何字节都可以通过小于区域长度的单个指针偏移量来取到。
- 连续的内存缓冲区:存储Array的多值组件的连续内存区域。有时称为“缓冲区”。
- 基本类型:占用固定大小的内存槽的数据类型,以位宽或字节宽度指定占用内存大小。
- 嵌套或参数类型:完整结构依赖于一个或多个其他子对象类型的数据类型。当且仅当子类型相等时,两个完全指定的嵌套类型相等。例如,如果U和V是不同的相对(简单)类型,List与List 也不同。
- 相对类型或简单类型(不合格):特定的基本类型或完全指定的嵌套类型。当我们说槽时,我们是指相对类型值,不一定是任意物理存储区域。
- 逻辑类型:使用某些相对(物理)类型实现的数据类型。例如,存储在16个字节中的十进制值可以存储在一个大小为16槽的字节数组中 。类似地,字符串可以存储为 List。
- 父和子数组:表示嵌套类型结构中物理值数组之间关系的名称。例如,List类型:父类型的数组有一个T型数组作为它的子元素(参见下面的列表)。
- 叶子节点或叶子:一个原始值数组,可能是也可能不是具有嵌套类型的某些数组的子数组。
要求,目标和非目标
基本要求
- 一种物理内存布局,可在处理平面和嵌套列式数据的各种系统之间进行零反序列化的数据交换,包括Spark,Drill,Impala,Kudu,Ibis,Spark,ODBC协议和利用开源组件的专有系统。
- 所有数组槽都可以在不间断的时间内访问,复杂性在嵌套级别上呈线性增长
- 能够表示完全物化和解码/解压缩的Parquet 数据
- 所有连续的内存缓冲区以64字节边界对齐,并填充到64字节的倍数。
- 任何相对类型都可以有空槽
- 数组一旦创建就不可变。实现可以提供API来突变数组,但应用突变将需要构建新的数组数据结构。
- 数组可重定位(例如,用于RPC /瞬态存储),无需调整指针。另一种方法是连续的内存区域可以迁移到不同的地址空间(例如通过memcpy类型的操作),而不改变它们的内容。
目标(对于本文档)
- 描述相对类型,足够的明确的描述实现(物理值类型和一组初始嵌套类型)
- 每个相对类型的内存布局和随机访问模式
- 空值表示
非目标(对于本文档)
- 枚举或指定可以实现为基本(固定宽度)值类型的逻辑类型。例如:有符号和无符号整数,浮点数,布尔值,精确小数,日期和时间类型,CHAR(K),VARCHAR(K)等。
- 指定标准化元数据或RPC或临时文件存储的数据布局。
- 定义选择或屏蔽向量(vector)构造
- 实现具体细节
- 用户或开发人员C/C++/Java API的详细信息。
- 任何由表命名的数组组成的“表”结构,每一个都有自己的类型,或任何构成数组的其他结构。
- 任何内存管理或引用计数子系统
- 枚举或指定编码或压缩支持的类型
字节顺序(Endianness)
默认情况下,Arrow格式是低位编址的(将低序字节存储在起始地址)。模式元数据有一个指明RecordBatches的字节顺序的字段。通常这是生成RecordBatch的系统的字节顺序。主要用例是在具有相同字节码的系统之间交换RecordBatches。首先,当尝试读取与底层系统不匹配的字节顺序的模式时,将会返回错误。参考实现集中在地位编址,并为此提供测试。最终我们可以通过字节交换来提供自动转换。
对齐和填充
如上所述,所有缓冲区都旨在以64字节边界为准对齐内存,并且填充到64字节倍数的长度。对齐要求遵循优化内存访问的最佳做法:
- 数值数组中的元素将保证通过对齐的访问来读取。
- 在一些架构上,对齐可以帮助限制部分使用的缓存行。
- 64字节对齐由英特尔性能向导为超过64个字节的数据结构所推荐的(这将是Arrow格式数组的共同情况)。 要求填充64个字节的倍数允许在循环中一致地使用SIMD指令,无需额外的条件检查。这样就允许更简单和更有效的代码。
选择特定的填充长度是因为它与2016年4月可用的最大的已知SIMD指令寄存器匹配(Intel AVX-512)。保证填充也可以允许某些编译器直接生成更优化的代码(例如可以安全地使用英特尔 -qopt-assume-safe-padding)。
除非另有说明,填充字节不需要具有特定值。
数组长度
任何数组具有已知且固定长度,存储为32位有符号整数,因此最多可以存储(2^31 - 1)个元素。我们选择一个有符号的int32有一下2个原因:
- 增强与Java和客户端语言的兼容性,可能对无符号整数具有不同的支持质量。
- 为了鼓励开发人员组成较小的数组(每个数组在其叶节点中都包含连续的内存),以创建可能超过(2^31- 1)个元素的更大数组结构,而不是分配非常大的连续内存块。
空值计数
空值槽的数量是物理数组的属性,并被认为是数据结构的一部分。空值计数存储为32位有符号整数,因为它可能与数组长度一样大。
空值位图
任何相对类型都可以有空值槽,不管是原始类型还是嵌套类型。
具有空值的数组必须具有连续的内存缓冲区,称为空(或有效)位图,其长度为64字节的倍数(如上所述),并且足够大,以至于每个数组槽至少有1位。
任何数组槽是否有效(非空)是在该位图的各个位中编码的。索引(设置位)j值为1表示该值不为空,而0(位未设置)表示该值为空。位图被初始化为在分配时间全部未设置(这包括填充)。
is_valid[j] -> bitmap[j / 8] & (1
indices: [0, 0, 0, 1, 1, 1, 0]
//['a','b']为字典值,索引为0;['c', 'd', 'e']为字典值,索引为2
dictionary i
type: List
[
['a', 'b'],
['c', 'd', 'e'],
]
|
|
|