内容提要
Apache Parquet存储固定长度列表(如向量嵌入)效率低,因Dremel编码开销大。Hardwood通过检测定义和重复级别流,识别固定长度列表并绕过常规解码,实现快速路径。列读取器提速达2.5倍,行读取器最高3.9倍,接近扁平列性能。该优化可选启用,预计1.1版默认开启。
延伸解读
为什么固定长度列表在Parquet中效率低
Parquet使用Dremel编码处理嵌套和重复数据,即使列表长度固定,也会为每个元素记录定义级别和重复级别。对于固定长度列表,重复级别流呈现周期性模式(如0,1,1,0,1,1...),这种冗余编码导致解码开销约为扁平列的三倍。社区正在讨论添加FIXED_SIZE_LIST逻辑类型,但尚未落地。
快速路径的检测原理
Hardwood通过扫描编码后的定义级别和重复级别流,检测数据页中的列表是否实际为固定长度。定义级别流若为全最大值,则O(1)判断无空值;重复级别流则通过模式匹配(如位打包的周期模式或RLE运行)识别固定长度。检测到后,绕过常规Dremel重建,直接按页复制值,避免生成级别数组,减少GC压力。
性能提升与适用场景
列读取器提速约2.5倍,行读取器最高3.9倍(列表长度64-256时),接近扁平列性能。短列表(如3元素)提升较小,长列表(如768维嵌入)提升显著。该优化对可变长度列表的检测开销极低(通常<0.1%),但列表长度为15时开销约2%。适用于存储向量嵌入、坐标等固定长度数据的场景。
启用方式与未来展望
该优化在Hardwood 1.1.0.Beta1中需显式启用,设置解析选项hardwood.fixed-list-fast-path为true,预计1.1正式版默认开启。一旦Parquet格式支持FIXED_SIZE_LIST,此优化对新写入文件不再必要,但对现有数据仍有价值。
Q&A
Parquet 存储固定长度列表(如向量嵌入)的主要问题是什么?
Parquet 使用 Dremel 编码来存储列表,即使列表长度固定,也会为每一行记录定义级别和重复级别,导致解码开销大,性能比扁平列存储慢约 3 倍。
Hardwood 如何实现固定长度列表的快速路径?
Hardwood 通过扫描编码的定义级别和重复级别流,检测数据页中的列表是否实际为固定长度。如果检测成功,则绕过常规的 Dremel 记录重建机制,直接按页复制值,避免逐元素处理和级别数组的物化。
Hardwood 的固定长度列表快速路径能带来多大的性能提升?
性能提升取决于列表长度和读取器类型。列读取器通常提速约 2.5 倍,行读取器在列表长度中等(约 64-256)时最高可达 3.9 倍,对于长列表(如 768 元素)也能达到接近扁平列的性能。
Hardwood 的固定长度列表快速路径如何检测重复级别流中的固定长度模式?
检测重复级别流时,根据列表长度 n 分为三种情况:n≤8 时使用位打包模式匹配;n≥16 时利用字节周期性和批量比较;9≤n≤15 时使用标量回退方法逐记录检查。检测器不硬编码特定模式,而是从第一条记录推导出步长并验证整个流是否重复该模式。
Hardwood 的固定长度列表快速路径目前如何启用?
该优化目前是可选启用的,需要设置解析器选项 hardwood.fixed-list-fast-path 为 true。预计在 1.1 版本中默认启用。
Hardwood 的固定长度列表快速路径对常规可变长度列表有性能影响吗?
检测逻辑本身有开销,但通常很小。对于 n≤8 的情况,检测开销约为常规列表检索成本的 1.7%;对于较大的 n,开销低于 0.1%。即使在最坏情况下(如 n=15),端到端解析时间的影响也仅为约 2%,通常可以忽略。
Parquet 社区计划如何从格式层面解决固定长度列表的存储效率问题?
Parquet 社区正在讨论添加新的逻辑类型 FIXED_SIZE_LIST,以原生支持固定长度列表,从而避免 Dremel 编码的开销,使解码性能达到扁平模式的水平。