Apache Parquet 中的自适应无损浮点编码
摘要
自适应无损浮点(ALP)编码是一种针对 Apache Parquet 中浮点数据的新轻量级编码,提供与 zstd 相似的压缩比,同时具有更快的解压速度、随机访问支持以及对 GPU/SIMD 友好的解码。
<p><a href="https://lobste.rs/s/dtozf3/adaptive_lossless_floating_point">评论</a></p>
查看缓存全文
缓存时间: 2026/09/23 20:53
# ALP:Apache Parquet 中的自适应无损浮点编码
来源:https://parquet.apache.org/blog/2026/09/22/alp-adaptive-lossless-floating-point-encoding-in-apache-parquet/
关于 ALP 的设计、性能以及在 Apache Parquet 生态系统中采用情况的技术概述。
Apache Parquet 新增了自适应无损浮点编码(ALP Encoding)(https://parquet.apache.org/docs/file-format/data-pages/alpencoding/)——一种新的轻量级浮点编码格式,其压缩率与 `zstd` (https://github.com/facebook/zstd) 相当,解压速度更快,支持随机访问,并且对 GPU 和 SIMD 指令友好。
---
ALP 最适用于以浮点类型(32 位 `FLOAT` 和 64 位 `DOUBLE`)存储的十进制数值,例如:
- 货币数值(汇率、公共资金、股票、价格等)——例如,`1.2345` 或 `22.03`
- 地理坐标(经度/纬度)——例如,`42.3584`,`-71.0598`
- 科学测量值(温度、压力、速度、角度等)——例如,`-273.15`,`9.81`,`3.14159`
ALP 不适用于指数范围宽或有效数字位数多的数据,例如通常覆盖整个浮点范围的向量嵌入。此类数据可继续使用现有的 Parquet 特性,如 `PLAIN` 或 `BYTE_STREAM_SPLIT` (https://parquet.apache.org/docs/file-format/data-pages/encodings/#BYTESTREAMSPLIT) 编码,然后使用 `ZSTD` 等通用压缩。
十进制数值可以使用 Parquet 的 `DECIMAL` 逻辑类型存储,但该类型要求预先声明精度和小数位数,且无法存储超出该范围的数值。因此,在数据确切格式未知的情况下,系统通常将十进制数值存储为 `FLOAT` 或 `DOUBLE`。例如,JavaScript 唯一的 *number* 类型是 `DOUBLE` (https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type),常用数据科学工具如 pandas 会将看似十进制的值推断为 `float64` (https://pandas.pydata.org/docs/reference/api/pandas.to_numeric.html),而 NumPy 根本没有十进制数据类型 (https://numpy.org/doc/stable/reference/arrays.dtypes.html)。
* JavaScript 也有 `BigInt` (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt),但它只能表示整数。
## 为什么需要 ALP?
编码浮点数据是一个复杂的工程问题,源于浮点值的本质。它们不能精确表示大多数实数,这导致了舍入误差,阻碍了 Delta 和 Frame of Reference 等现有轻量级编码的使用。
在 ALP 之前,对于 Parquet 中的 `FLOAT`/`DOUBLE` 值,`BYTE_STREAM_SPLIT` (https://parquet.apache.org/docs/file-format/data-pages/encodings/#BYTESTREAMSPLIT) 是 `PLAIN` 唯一的非字典替代方案。它并不减小数据大小,但*可以*在后续使用重量级压缩器时提高压缩率和速度。
重量级压缩有效减少了数据大小,但代价是:
- 解码速度——解压速度通常是数据访问的瓶颈。
- 随机访问——读取一个值需要解码包含可能数千个其他值的整个数据页。
- 数据依赖——可变长度压缩意味着解码一个值需要解码前面的值,难以利用 SIMD 指令 (https://en.wikipedia.org/wiki/SIMD) 和 GPU (https://en.wikipedia.org/wiki/Graphics_processing_unit) 等现代硬件进行并行化。
ALP 旨在解决常见数据模式下的所有这三个问题,同时实现与重量级压缩相当的压缩率。
Parquet 先应用编码,然后可选地作为单独步骤应用压缩编解码器。下面的图表比较了 `PLAIN` 和 `BYTE_STREAM_SPLIT` 编码后跟 `ZSTD` 压缩,与 `ALP` 编码且无额外压缩的效果。用户可以期望 ALP 解码速度快 `10x`,获取单个值速度快数千倍,压缩率略低,压缩速度略快。1 (https://parquet.apache.org/blog/2026/09/22/alp-adaptive-lossless-floating-point-encoding-in-apache-parquet/#fn:1)
平均压缩率基准测试
平均压缩速度基准测试
平均解压缩速度基准测试
平均随机访问基准测试
**图 1**:`PLAIN+ZSTD` 和 `BYTE_STREAM_SPLIT+ZSTD`(每种编码后跟每页 `ZSTD` 压缩),以及 `ALP`(无压缩编解码器)在 `3` 台机器上的 `30` 个数据集上的平均压缩率、压缩速度、解压缩速度和随机访问速度。数值越高越好。随机访问速度通过从 `city_temperature_f` 中解码 `100` 个确定性、均匀分布的行来测量。
请注意,这些数字来自 ALP 的预发布 Rust 实现,我们预计随着实现的优化和调优,性能会有所提升。即便如此,尽管 `zstd` 实现已经过多年的优化工作,ALP 在许多情况下已经比 `zstd` 更快。我们还在 C++ 实现 (https://docs.google.com/spreadsheets/d/1NmCg0WZKeZUc6vNXXD8M3GIyNqF_H3goj6mVbT8at7A/) 上测量到了类似的改进。
## 技术概述
ALP 利用了一种常见模式:许多存储为 `FLOAT` 或 `DOUBLE` 的值源自十进制数字,且有效数字相对较少,例如价格或测量值。本节解释了 ALP 背后的直觉,然后更详细地介绍了编码和解码流程。
ALP 将浮点值分批编码,称为“向量”,大小范围从 `8` 到 `32K` 个值(例如,`1024`)。向量中的每个值被编码为一个整数,并且该向量存储两个由所有值共享的整数参数:一个“指数”(`e`)和一个“因子”(`f`)。每个向量可以使用不同的指数和因子。下文将解释它们的选择方式。原始值通过以下计算恢复:
```
value = encoded × 10^f × 10^{-e}
```
此计算使用浮点算术,会四舍五入到最接近的可表示值,因此可能无法完全重现原始值。当发生这种情况时,ALP 会将原始的全精度值单独存储为“异常”,从而保持编码的无损性。特殊值如 `NaN`、`±Infinity` 和 `-0.0` 也作为异常存储。
在每个向量内,编码值通过减去最低值(帧参考)然后位压缩到固定宽度来存储。异常直接存储在编码数组之后。每个 ALP 向量的布局如下所示。
ALP 序列化向量布局
**图 2**:序列化 ALP 向量的布局:一个固定大小的向量头,后跟一个可变大小的数据部分。
由于每个值都存储为固定宽度的位压缩整数,定位任意行只需要计算其编码位的偏移量。对该整数应用帧参考、指数和因子即可恢复原始浮点值。最后,检查目标行的异常索引,如果存在异常,则返回其值。
选择好的指数和因子是 ALP 性能的关键。每个 Parquet 写入器可以自由使用任何算法为每个向量选择它们。Parquet 规范提供了一个基于采样的示例算法,旨在最小化编码大小。通常,选择指数以捕获向量中的大多数十进制数字,同时最小化异常;选择因子以尽可能多地移除尾随零。
最后,ALP 在位压缩前,从每个编码值中减去最小编码值(帧参考),进一步减少了所需的位数。
编码流程很简单,如下例所示,展示了如何编码一个向量:
ALP 编码流程示例
**图 3**:使用 ALP 编码一个包含 `1024` 个 64 位浮点值的向量。
要编码此向量,首先选择参数 `e = 4` 和 `f = 3`。然后使用公式 `encoded = round(value × 10^4 × 10^{-3})` 将值转换为整数。每个整数通过反向转换 `decoded = encoded × 10^3 × 10^{-4}` 进行检查。未成功往返(round-trip)的值,如 `8.0605123`(解码为 `8.1`),存储在异常数组中。向量中的最小值 `3335` 成为帧参考,并从每个整数中减去,然后使用 `15` 位对生成的差值进行位压缩。在此示例中,ALP 使用 `1920` 字节存储位压缩的差值,加上一个 `13` 字节的向量头和异常空间。`PLAIN` 对相同的 `1024` 个值使用 `8192` 字节。此比较排除了两种编码的页级元数据。有关参数选择方式以及舍入和异常处理工作原理的更多详细信息,请参见 ALP 编码规范 (https://parquet.apache.org/docs/file-format/data-pages/alpencoding/)。
解码向量需要类似的步骤,但顺序相反,如下所示。
ALP 解码流程示例
**图 4**:使用 ALP 将包含 `1024` 个值的向量解码回浮点值。
首先,解包位压缩的差值,然后通过 `original = (3335 + delta) × 10^3 × 10^{-4}` 计算原始值。然后,通过用异常值覆盖输出数组中的异常位置来“修补”任何异常。
## 致谢
ALP 首次发表于 Azim Afroozeh、Leonardo Kuffó 和来自 CWI (https://www.cwi.nl/en/research/database-architectures/) 数据库架构组的 Peter Boncz 的 SIGMOD 2024 论文 (https://dl.acm.org/doi/10.1145/3626717) 中。Vortex (https://vortex.dev/) 和 Lance (https://lance.org/) 格式早期采用了 ALP,展示了其在工业应用中的优势。2025 年底,社区开始了标准化进程。除了本文作者,许多社区成员也做出了贡献,包括 Divjot Arora、Arnav Balyan、Devan Benz、Ryan Blue、Alkis Evlogimenos、Xuwei Fu、Vinoo Ganesh、Adrian Garcia Badaracco、Curt Hagenlocher、Amogh Jahagirdar、Micah Kornfield、Robert Kruszewski、Julien Le Dem、Kevin Liu、Steve Loughran、Ismaël Mejía、Antoine Pitrou、Adam Reeve、Ed Seidl、Russell Spitzer、Matt Topol、Jeffrey Vo、Daniel Weeks、Gang Wu 和 Zehua Zou。
## 生态系统采用
该编码作为 parquet-format 2.14.0 (https://parquet.apache.org/blog/2026/09/11/2.14.0/) 的一部分于 2026 年 9 月发布。ALP 已在至少一个主要的开源实现(`parquet` 60.0.0 (https://crates.io/crates/parquet/60.0.0) Rust crate)中得到支持,我们预计其他 Parquet 实现将在未来几个月内添加支持。请查看实现状态 (https://parquet.apache.org/docs/file-format/implementationstatus/) 页面了解当前的支持状态。您也可以今天就在您自己的数据集上使用 ALP 基准测试仓库 (https://github.com/alamb/alp_benchmark#run-on-your-own-parquet-files) 中的工具进行尝试。
## 结论
ALP 为浮点数据带来了快速、可并行化的解码和实用的随机访问功能,以标准形式提供,任何支持该编码的 Parquet 实现都可以读取。它的加入是 Apache Parquet 不断演进以满足现代数据系统需求的又一例证。
与 Parquet 的所有新增功能一样,这是一项社区协作的成果,许多个人和供应商共同努力,就共同标准达成一致,并产生了文档完备的规范和多个参考实现。我们预计 ALP 将在未来几年内被 Parquet 生态系统广泛采用。
## 资源
- **ALP 编码规范** (https://parquet.apache.org/docs/file-format/data-pages/alpencoding/)
- **Apache Parquet 格式规范** (https://github.com/apache/parquet-format)
- **实现状态页面** (https://parquet.apache.org/docs/file-format/implementationstatus/)
相似文章
ALP: 自适应无损浮点压缩
本文介绍ALP,一种针对IEEE 754浮点数据的最先进的无损压缩算法,利用十进制和高精度模式。它在解码速度、压缩比和压缩速度方面表现出色,获得了SIGMOD最佳构物奖。
Parquet 中定长列表的快速路径
这篇博客文章介绍了 Apache Parquet 的一项优化,用于高效存储和解码像向量嵌入这样的定长列表,通过绕过固定大小数据页的 Dremel 重构,实现了与扁平列相当的解码性能。
将Postgres数据以Parquet格式存储在S3上:LTAP架构解析
Databricks推出Lakebase LTAP架构,将Postgres数据以Parquet格式存储在S3上,无需CDC或镜像即可在单份数据上实现事务与分析。
CubicQuant:面向1-8位权重高吞吐量LLM推理的参数化非均匀码本
CubicQuant提出了一种用于LLM权重的参数化非均匀标量量化格式,利用单调三次曲线在1-8位宽度下自适应重建水平,同时保留密集整数码流以提升GPU执行效率。实验表明,与均匀基线和浮点基线相比,RMSE有所降低,并给出了初步的H200内核测量结果。
FAMPWQ:基于Fisher信息的自适应混合精度权重量化,用于高效LLM推理
FAMPWQ提出了一种基于Fisher信息的自适应混合精度权重量化方法,用于在资源受限设备上实现高效LLM推理,在困惑度、准确性和判断比较方面显著优于基线方法。