Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions content/en/blog/features/alp_encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
---
title: "ALP lightweight floating-point encoding in Apache Parquet"
date: 2026-07-08
description: "The consensus building machine strikes again"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The consensus building machine strikes again

I like this title subtitle, but I fear it may obscure the overall message of the blog post. Maybe something that focused on the benefits of ALP could be better -- for example something like

"Fast, random access, GPU and SIMD-friendly compression and decompression; similar in size to ZSTD"

author: "[Konstantin Tarasov](https://github.com/sdf-jkl)"
categories: ["features"]
---

## What is ALP?

ALP (Adaptive Lossless floating-Point) is a new lightweight floating-point encoding that shows a better compression ratio than heavyweight compressors like zstd **and** faster [de]compression speed than other lightweight floating-point encodings.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALP (Adaptive Lossless floating-Point) is a new lightweight floating-point encoding that shows a better compression ratio than heavyweight compressors like zstd and faster [de]compression speed than other lightweight floating-point encodings.

I think a better characterization is probably that it shows a similar compression ratio (TODO we should verify with the paper).

Another key benefit of ALP is that it supports "random access" -- a single value can be deocded without decoding the entire page. This property is becoming increasingly important for increasingly important workloads such as point lookups with text and vector indexes.

You touch on this with the Why but I think it would be good to put it in the intro too


It was developed by the folks at [CWI](https://www.cwi.nl/en/research/database-architectures/) and uses smart tricks to represent floating-point data as integers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was developed by the folks at CWI and uses smart tricks to represent floating-point data as integers.

Perhaps it would be better to list the full name "Computer Architectures Group at CWI" rather than "the folks"


ALP shines at data like:
- Monetary values (exchange rates, public funds, stocks, prices, etc.)
- Geographic coordinates (longitude/latitude)
- Scientific measures (temperature, pressure, speed, degrees, energy, etc.)
- Timestamps stored as floating-point

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALP shines at data like:

The examples here are great and I suggest you keep them.

I also think it would also help to summarize the general characteristics of data that ALP works well on. Specifically I think ALP works well for values stored using Floating point types but that do not need a full 32 or 64 bits of precision, such as limited range integers and decimal values. All your examples I think fall into this class


## Why ALP?

### Better physical data representation

Better, faster, stronger? But how much?

<!-- Compression ratio table here I think the paper one looks better, but it's not really exactly the same -->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree that before publishing this blog, we should get some real numbers / a chart. I know Prateek has some in the original spec https://docs.google.com/document/d/1PlyUSfqCqPVwNt8XA-CfRqsbc0NKRG0Kk1FigEm3JOg/edit?tab=t.0#heading=h.5xf60mx6q7xk,

or maybe we can create some new numbers / charts as part of the post (with the Rust encoder/decoder or the C++ one)

The key metrics to report for several datasets

  1. Compression/Decompression speed (in GB/sec) and encoded size
  2. Compare ALP with no compression, and PLAIN encoding with ZSTD compression, and PLAIN encoding with Snappy compression (default in duckdb)


[De]compression speed improvement depends on the Parquet reader/writer implementation, but users can expect performance similar to what the paper reports.

<!-- Compression/decompression table here -->

### The most efficient way to read is to not read at all

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think highlighting the feature (Random Access) would be good

Suggested change
### The most efficient way to read is to not read at all
### Random Access: The most efficient way to read is to not read at all


One of the [gripes users have with Parquet](https://sympathetic.ink/2025/12/11/Column-Storage-for-the-AI-era.html) is that it is not optimized for random access. The minimum amount of data you need to decompress and decode in Parquet is usually an entire page.

ALP encoding avoids that by splitting the page into vectors (usually 1024 values each, sized for SIMD and for fitting in L1 cache). Each vector stores the necessary metadata to decode any row inside.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALP encoding avoids that by splitting the page into vectors (usually 1024 values each, sized for SIMD and for fitting in L1 cache). Each vector stores the necessary metadata to decode any row inside.

The finalized spec in apache/parquet-format#557

| 2 | log_vector_size | 1 byte | uint8 | log2(vector_size). Must be in the inclusive range [3, 15]. Recommended default: 10 (vector size 1024) |

Allows for vectors between 8 and 32K sizes, so I think we can make this more specific like

"ALP encoding avoids that by encoding values into one or more vectors (between 8 and 32K, defaults to 1024, chosen by the writer), sized for SIMD and for fitting in L1 cache). Each vector stores the necessary metadata to decode any row inside."


**Before**, reading a single value:

> Load the whole page → decompress and decode all of it → retrieve the value
**After**, with ALP:

> Load just the page metadata → get the vector offset → decode only that vector
This introduces a finer read granularity than the Parquet page and can significantly speed up single-row decodes and random access.

## Technical overview

ALP takes inspiration from [PDE (Pseudodecimal encoding)](https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/papers/btrblocks.pdf) and heavily improves on it.

PDE works by finding the smallest integer, paired with a decimal exponent, that represents the floating-point value exactly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend we refocus the text on the details of ALP as it is incorporated into Parquet rather than the history / connection to PDE (people can refer to the ALP academic paper for the contrast to PDE). I like the example you have here -- maybe we could expand that idea with a worked example by picking a few other values and walking the reader through how it would be encoded

For example, I think we could choose the following set where some can be lossless encoded with exponent/factors but where there will be two exceptions (to cover the two classes): NaN and a large value

100.2
100.52
101.1
100.48
-999999999999.0
100.2
NaN

Then, at the appropriate step in the "ALP Encoding pipeline in Parquet", you could show how those values are encoded, how it would choose of e and f for the data, and then make a diagram of how it would then be encoded and packed, and then show how decoding works.

I can help make diagrams / figures for this if you would like (I love a good diagram... or 3!)


For example:

> 8.0605 can't be physically represented in [IEEE 754](https://ieeexplore.ieee.org/document/8766229) as is, and is instead approximated as 8.06049999999999933209.
>
> PDE takes the value and brute-force searches over exponents, computing `significand = round(value × 10^e)` for each.
>
> For this value it arrives at significand 80605 with exponent 4.
The catch is that PDE stores an exponent for every single value.

The key ideas behind ALP are:
- the majority of floating-point values share similar decimal precision within vectors
- there is a huge benefit in vectorizing the encoding

The first idea leads to an improvement over PDE. Instead of storing an exponent per value, we store it per vector. This way we get a much smaller metadata footprint.

The second idea uses SIMD intrinsics to make the code as parallelizable as possible to achieve greater [de]compression speeds.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" The second idea uses SIMD intrinsics"

FWIW I think the SIMD intrincisc is a specific implementation detail of C++ that might or might not apply to each reader and writer implementation. I suggest instead we focus the post more on the fact that the encoding and decoding are "SIMD-friendly" and readers and writers can achieve very high compression and decompression.


### ALP Encoding pipeline in Parquet

The way that ALP encodes data is:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALP Encoding pipeline in Parquet

I also think we should add a section for "ALP Decoding Pipeline" that shows how to decode the encoded values from the previous section. That will let us both explain more how it works as well as illustrate the properties above (its compactness and that you could decode a single row)

Also with diagrams :) (and again I could help)


Input: float/double array
|
v
+----------------------------------------------------------+
| 1. CHOOSE PARAMETERS |
| Select (exponent, factor) pair for this vector |
+----------------------------------------------------------+
|
v
+----------------------------------------------------------+
| 2. DECIMAL ENCODING |
| encoded[i] = fast_round(value[i] * 10^e * 10^(-f)) |
| Detect exceptions where decode(encode(v)) != v |
+----------------------------------------------------------+
|
v
+----------------------------------------------------------+
| 3. FRAME OF REFERENCE (FOR) |
| min_val = min(encoded[]) |
| delta[i] = encoded[i] - min_val |
+----------------------------------------------------------+
|
v
+----------------------------------------------------------+
| 4. BIT PACKING |
| bit_width = ceil(log2(max_delta + 1)) |
| Pack each delta into bit_width bits |
+----------------------------------------------------------+
|
v
Output: Serialized vector bytes

**1. Choose parameters**

First, sample multiple vectors within a row group to find the overall best combinations of exponent and factor.

Then re-evaluate those candidate combinations on each vector to pick the pair that compresses it best.

**2. Decimal encoding**

Encode each value with the formula above, then round-trip it (decode it back) to make sure it matches the original.

If the round-trip fails, the value is added to the vector's exceptions list.

**3. Frame of reference (FOR)**

Once the values are encoded as integers, we pick the smallest one as the frame of reference. Every value is then stored as its delta from that minimum.

**4. Bit packing**

Finally, we take the bit width of the largest delta and pack every delta into that many bits.


### ALPrd

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ALPrd"

I think the ALPrd mention, while correct, will distract from the focus on Parquet. I suggest we frame this more on what cases ALP won't work well for.

For exmaple, something like

not all floating point data can be exploited by ALP, for example vector embeddings typically span the full floating point range and do not encode well with ALP. Such usecases can continue to Apache use existing Parquet features such as PLAIN or BYTE_STREAM_SPLIT encoding followed by ZSTD or SNAPPY general purpose decompression.


The ALP paper also introduces an ALPrd encoding, used when ALP can't compress enough of the values in a row group. Apache Parquet already provides a similar mechanism via BYTE_STREAM_SPLIT + ZSTD, so ALPrd was not included.

## Ecosystem adoption

The paper was published in 2024 and now has a sibling [G-ALP](https://ir.cwi.nl/pub/35205/35205.pdf) optimized for GPUs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ecosystem adoption

I would recommend fousing this section on the ALP adoption within the Parquet community (and maybe here highlight the efforts of the community to get it ready, and that we now have implementations in the open source C++, Java, and Rust implementations that we expect to ship in the next several months

We can probably consolidate the mentions of other systems adopting ALP in the intro section as additional motivation for adopting it in Parquet. Perhaps something like "ALP has already been implemented in new formats such as Vortex, several academic systems such as BtrBlocks, and F3, and FastLanes, and in tightly integrated open source systems such as the DuckDB file format (which originated at the same CWI lab as ALP)."


Since then ALP has been adopted as the default floating-point encoding by [DuckDB](https://duckdb.org/2024/02/13/announcing-duckdb-0100#adaptive-lossless-floating-point-compression-alp) and [CedarDB](https://cedardb.com/blog/release_notes/early_2026/#better-compression-less-storage-floats-and-text), and implemented as a codec by [ClickHouse](https://github.com/ClickHouse/ClickHouse/pull/91362).

It has also been adopted by the new file formats like [Vortex](https://github.com/vortex-data/vortex), [F3](https://github.com/future-file-format/f3) and [FastLanes](https://github.com/cwida/fastlanes) (created by the same folks as ALP).

These formats aspire to dethrone Parquet, some (Vortex) say - ["Parquet is for the floor"](<!--I'll get a picture and add a link here -->).

This comment may sound mean, but what it really means is that Parquet stands as a baseline and a benchmark for the new contenders.

The new file formats improve rapidly because they don't have to worry about backwards compatibility for a giant userbase. Parquet, however, does not stand still: it's a moving target. It takes more time, but it learns from the new formats and adopts their best ideas.

## Conclusion

ALP brings fast, parallelizable decoding and practical random access to floating-point data in Parquet. It's one more step toward closing the gap with the newest file formats.

## Resources

- [ALP paper (CWI)](https://ir.cwi.nl/pub/33334/33334.pdf)
- [Column Storage for the AI era](https://sympathetic.ink/2025/12/11/Column-Storage-for-the-AI-era.html)
- [Column Storage for the AI era slides - file formats encoding parity slide](https://docs.google.com/presentation/d/19F-XvNJ8sgIpIeIduA3PhbsWp4pC-P632J2eJV1cLG8/edit?slide=id.g3b660485fe2_0_9#slide=id.g3b660485fe2_0_9)
- [ALP Parquet encoding PR](https://github.com/apache/parquet-format/pull/557)
- [BtrBlocks paper](https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/papers/btrblocks.pdf)
Loading