Skip to content

Blocked BitPacked: store block offsets as a child - #10004

Merged
mhk197 merged 4 commits into
developfrom
mk/bitpacked-stack-03-width-child
Oct 5, 2026
Merged

mhk197 merged 4 commits into
developfrom
mk/bitpacked-stack-03-width-child

Conversation

@mhk197

@mhk197 mhk197 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Tracking Issue: #10167

Summary

Add an optional block_offsets child to BitPackedArray that stores the byte boundaries of independently sized packed blocks, and make the shared bit width in BitPackedData optional.

An array has exactly one of the two: a global bit width, as today, or block offsets.

Design

Bit widths

BitPackedData::global_bit_width is Some(width) when every block shares a width, and None when block_offsets is Some. Validation requires exactly one of them.

BitPackedArrayExt::bit_widths() replaces bit_width():

pub enum BitWidths {
    /// Every block is packed at this bit width.
    Global(u8),
    /// Byte boundaries of the packed blocks, from which each block's bit width is derived.
    Blocked(ArrayRef),
}

Paths that need one width (decoding, scalar_at, v1 serialization and CUDA) return an error for blocked arrays until the decoder follow-ups.

There is a viewed and owned version of this.

Block offsets

block_offsets is a non-nullable unsigned integer array with num_blocks + 1 entries, including a trailing end boundary.

Boundaries are relative to the first entry. Block i occupies bytes block_offsets[i] - block_offsets[0] to block_offsets[i + 1] - block_offsets[0] of the packed buffer, at (block_offsets[i + 1] - block_offsets[i]) / 128 bits, because a 1024-value block at width w takes 128 * w bytes.

For example, three blocks at widths 3, 0 and 7 have offsets [0, 384, 384, 1280]. Offsets with equal steps are still blocked; nothing converts them to a global width.

Validation

  • A global width must be at most ptype.bit_width() (previously at most 64), and the packed buffer must hold num_blocks * 128 * width bytes, as before.
  • Block offsets must have a non-nullable unsigned integer dtype and num_blocks + 1 entries. Host-resident primitive offsets are also checked boundary by boundary: they must be nondecreasing, each block must be a whole number of 128-byte rows at most ptype.bit_width() bits wide, and together they must span exactly the packed buffer. Other encodings and device-resident offsets only get the dtype and length checks.

Scope

Nothing in this PR produces block offsets, but arrays built with try_new_with_block_offsets can be constructed and inspected. Kernels decline them, and decoding, scalar access, serialization and CUDA return an error.

Wire format and compatibility

fastlanes.bitpacked is unchanged. A global-width array has no block offsets child, so it serializes exactly as before, and the reader builds a global-width array from the metadata bit_width. Serializing a blocked array with this plugin returns an error. Compression goldens are unchanged.

API changes

  • BitPackedArrayExt::bit_width() and BitPackedData::bit_width() are replaced by BitPackedArrayExt::bit_widths() -> BitWidths.
  • BitPackedDataParts::bit_width: u8 is replaced by bit_widths: BitWidths.
  • BitPacked::try_new_with_block_offsets constructs an array from packed data and block boundaries. BitPacked::try_new and BitPackedData::try_new are unchanged.
  • BitUnpackedChunks::try_new and both unpacked_chunks accessors return an error for blocked arrays.
  • bitpack_decompress::unpack_single returns VortexResult<Scalar>.
  • The unused max_packed_value() accessor is removed.
  • The Python FastLanesBitPackedArray.bit_width property returns int | None.

@mhk197
mhk197 added this pull request to stack #10006 September 23, 2026 18:22
@codspeed

codspeed Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 41.92%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 2142 untouched benchmarks
⏩ 476 skipped benchmarks1
🗄️ 1 archived benchmark run2

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Simulation take[small_m/shuffled/primitive/nonnull/chunks=2048/indices=64] 279.4 µs 481 µs -41.92%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mk/bitpacked-stack-03-width-child (9c01ab9) with develop (9a17a1d)

Open in CodSpeed

Footnotes

  1. 476 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. 1 benchmark was run, but is now archived. If it was deleted in another branch, consider rebasing to remove it from the report. Instead if it was added back, click here to restore it. ↩

@mhk197
mhk197 removed this pull request from stack #10006 September 23, 2026 19:43
@mhk197
mhk197 added this pull request to stack #10012 September 23, 2026 19:43
@mhk197
mhk197 force-pushed the mk/bitpacked-stack-03-width-child branch from 6dc1734 to 9dc388a Compare September 23, 2026 19:43
@mhk197 mhk197 changed the title refactor: add a bitpacked width child refactor: add bitpacked chunk offsets child Sep 23, 2026
Base automatically changed from mk/bitpacked-stack-01-wire-boundary to develop September 30, 2026 19:57
@mhk197
mhk197 force-pushed the mk/bitpacked-stack-03-width-child branch from 9dc388a to 505f31f Compare October 1, 2026 15:43
@mhk197 mhk197 changed the title refactor: add bitpacked chunk offsets child refactor: store bitpacked block offsets as a child Oct 1, 2026
@mhk197
mhk197 marked this pull request as ready for review October 1, 2026 18:28
@mhk197 mhk197 added the changelog/break A breaking API change label Oct 1, 2026
@mhk197
mhk197 requested a review from joseph-isaacs October 1, 2026 18:28
@mhk197 mhk197 changed the title refactor: store bitpacked block offsets as a child Blocked BitPacked: store bitpacked block offsets as a child Oct 1, 2026
@mhk197
mhk197 requested a review from robert3005 October 1, 2026 18:29
@mhk197
mhk197 marked this pull request as draft October 1, 2026 20:32
@mhk197 mhk197 changed the title Blocked BitPacked: store bitpacked block offsets as a child Blocked BitPacked: store block offsets as a child Oct 1, 2026
@mhk197
mhk197 marked this pull request as ready for review October 1, 2026 22:31
@mhk197
mhk197 removed this pull request from stack #10012 October 2, 2026 02:46
Comment thread encodings/fastlanes/src/bitpacking/array/mod.rs Outdated
Comment thread encodings/fastlanes/src/bitpacking/array/mod.rs Outdated

/// Check that each block between `boundaries` is a whole number of 128-byte rows of at most
/// `max_bit_width` bits, and that the boundaries span `packed_len` bytes.
fn validate_primitive_offsets<T: Copy + Display>(

@mhk197 mhk197 Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is very expensive. It is necessary to do a full pass of blocked offsets to make sure the bitpacked array is valid unfortunately, since it depends on array values now as well as metadata

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Additionally, I expect this check to rarely run since the block offsets will be compressed

@mhk197
mhk197 force-pushed the mk/bitpacked-stack-03-width-child branch 2 times, most recently from 0304db0 to 5f3f2c3 Compare October 2, 2026 19:05
@mhk197
mhk197 added this pull request to stack #10243 October 2, 2026 21:14
/// Every block is packed at this bit width.
Global(u8),
/// Byte boundaries of the packed blocks, from which each block's bit width is derived.
Blocked(ArrayRef),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

make it a view?

Suggested change
Blocked(ArrayRef),
Blocked(&'a ArrayRef),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or have a viewed version

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll make this into BitWidthsView enum and add an owned BitWidths enum for when we need to do into_parts

fn bit_widths(&self) -> BitWidths {
match (self.global_bit_width, self.block_offsets()) {
(Some(bit_width), None) => BitWidths::Global(bit_width),
(None, Some(block_offsets)) => BitWidths::Blocked(block_offsets.clone()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So we don't need this clone

mhk197 added 3 commits October 5, 2026 11:39
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 force-pushed the mk/bitpacked-stack-03-width-child branch from 5f3f2c3 to ad8ba8c Compare October 5, 2026 15:42
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 merged commit e8e48c8 into develop Oct 5, 2026
93 of 94 checks passed
@mhk197
mhk197 deleted the mk/bitpacked-stack-03-width-child branch October 5, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/break A breaking API change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blocked BitPacked: Transition from scalar bit_width to block_offsets child

2 participants