Skip to content

bench: add MutableBuffer::extend, enhance MutableArrayData - #11742

Open
neilconway wants to merge 2 commits into
apache:mainfrom
neilconway:neilc/bench-mutable-buffer-extend
Open

neilconway wants to merge 2 commits into
apache:mainfrom
neilconway:neilc/bench-mutable-buffer-extend

Conversation

@neilconway

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

Extend the benchmarks for MutableBuffer::extend to cover more iterator shapes, and extend the MutableArrayData benchmarks to cover more data types. We also revise how data is generated for the zip_kernels and mutable_array benchmarks: the previous approach repeatedly passed the same input data to the functions on every iteration. This allows the branch predictor to do much better than it would on real-world data, which initially hid the ARM regression described above.

These improvements are intended to evaluate #11656 more accurately.

What changes are included in this PR?

  • Add an arrow-buffer mutable_buffer_extend benchmark that appends 32Ki items, one extend call per chunk, with chunks of random length (16 or 1024 items on average). It covers copied and mapped slices, an i32 range, repeat_n, a Chain of two slices, and a Filter, whose size hint is not exact.
  • Add Utf8View and dictionary cases to mutable_array, with the existing long random slices and with slices of 1 to 8 rows. Every case now starts with room for all the rows it copies, so its time goes to the copies rather than to growing the buffers.
  • Cycle zip_kernels, mutable_array and mutable_buffer_extend through 16 to 64 inputs instead of reusing one. Scalars and the constant masks stay the same, as they would across the batches of a query.

Are these changes tested?

Benchmark-only change.

Are there any significant user-facing changes?

No.

AI usage

Claude Code (Opus 5.5), reviewed with Codex (Astra 6.1).

…ata` benchmarks, with new inputs each iteration

`MutableBuffer::extend` had one benchmark, which copies `u32` slices.
Add an arrow-buffer benchmark that appends 32Ki items, one `extend` call
per chunk, with chunks of 16 or 1024 items on average. It covers these
iterator shapes:

- copied slices of `u8`, and mapped slices of `u8`, `i32` and `u128`.
- an `i32` range.
- `repeat_n`.
- a `Chain` of two slices.
- a `Filter`, whose size hint, unlike the others', is not exact.

`mutable_array` only covered `Utf8`. Add `Utf8View` and dictionary
cases, with the existing long random slices and with slices of 1 to 8
rows. Every case now starts with room for all the rows it copies, so its
time goes to the copies rather than to growing the buffers from 5 rows.

`zip_kernels` and `mutable_array` ran every iteration on the same input.
The CPU's branch predictor then learns that input's exact sequence of
branch outcomes, such as which string views are stored inline or how
long each copied run is. A query sees each batch once, so it never gets
that benefit, and code that branches on the data looks faster than it
would be in a query.

On an AMD EPYC Milan server, zipping two arrays of 8192 string views of
3 to 19 bytes with an all-true mask (one copy, with a branch per view on
whether it is inline) took about 3 branch misses per call when every
call used the same arrays. Cycling through 32 or more pairs of arrays
raised that to about 3,400, and the call took 3.4x as long. The
predictor memorized up to 32Ki to 64Ki views, depending on the build,
and almost none at 128Ki. With lists of 1024 slices of 1 to 8 rows, it
memorized up to 8 lists; from 32 lists on, it missed about once per
slice.

Every data input now changes each iteration, cycling through:

- `zip_kernels`: 64 arrays for each array input and 64 masks for each
  random mask case (512Ki rows).
- `mutable_array`: 64 arrays and 64 lists of slices.
- `mutable_buffer_extend`: 16 splits of the items into chunks, and 16
  arrays of 32Ki items for each case that reads input data (512Ki
  items).

Scalars and the constant masks stay the same on every iteration, as they
would across the batches of a query.

`mutable_buffer_extend` chunks have random lengths, 1 to 31 or 1 to
2047 items. With fixed lengths, the predictor would learn when each
call's loop ends, and a loop that handles several items per pass would
never have items left over.
@neilconway neilconway changed the title bench: add MutableBuffer::extend and view/dictionary `MutableArrayD… bench: add MutableBuffer::extend, enhance MutableArrayData Oct 20, 2026

@mbutrovich mbutrovich left a comment

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.

I already gave these multiple thorough reviews in #11656. Thanks @neilconway!

@Rich-T-kid

Copy link
Copy Markdown
Contributor

going to merge since these are just benchmarks

@Rich-T-kid
Rich-T-kid enabled auto-merge October 20, 2026 16:57
@github-actions github-actions Bot added the arrow-core Core crates related to representing arrow data label Oct 21, 2026
@Rich-T-kid
Rich-T-kid added this pull request to the merge queue Oct 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Oct 21, 2026
@Rich-T-kid
Rich-T-kid added this pull request to the merge queue Oct 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 22, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow-core Core crates related to representing arrow data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants