Repository navigation
Optimize byte-array dictionary gathering in arrow-row - #12113
Conversation
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing perf-dictionary-gather-12053 (9a086f3) to 8473490 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: dictionary
shards: 1
Results will be posted when all workers finish. File an issue against this benchmark runner |
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing perf-dictionary-gather-12053 (9a086f3) to 8473490 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: dictionary
shards: 1
Results will be posted when all workers finish. File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Target: row_format Comparing perf-dictionary-gather-12053 (9a086f3) to 8473490 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: dictionary
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-11fep6rz-1e90e640-9m46 Instance: c4a-highmem-16 (12 vCPU / 65 GiB) uname: BENCH_COMMAND: cargo bench --features=arrow,async,test_common,experimental,object_store --bench row_formatCPU Details (lscpu)Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Target: row_format Comparing perf-dictionary-gather-12053 (9a086f3) to 8473490 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: dictionary
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-11fep6rz-4c5b963b-gqsw Instance: c4a-highmem-16 (12 vCPU / 65 GiB) uname: BENCH_COMMAND: cargo bench --features=arrow,async,test_common,experimental,object_store --bench row_formatCPU Details (lscpu)Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
| } | ||
|
|
||
| #[test] | ||
| fn test_gather_dictionary_byte_values() { |
There was a problem hiding this comment.
I think we can simplify this test, all binary types go through gather_dictionary_byte_values so just testing the stringArray values is fine
There was a problem hiding this comment.
Thanks for reviewing this. I’ve simplified test_gather_dictionary_byte_values to use StringArray, since the string and binary variants share the same gather implementation. The test still covers sliced keys, null keys, and keys that reference null dictionary values.
| } | ||
| } | ||
|
|
||
| fn gather_dictionary_byte_values<K: ArrowDictionaryKeyType, T: ByteArrayType>( |
There was a problem hiding this comment.
can we add a small doc comment here
There was a problem hiding this comment.
Added a short doc comment to gather_dictionary_byte_values. It explains how the helper gathers values in dictionary-key order, preserves nulls, and returns an error if the accumulated offsets overflow.
Which issue does this PR close?
Closes #12053.
Rationale for this change
The sparse-dictionary encoding path gathers one dictionary value per input row before encoding. The current gather performs a dynamic MutableArrayData extension for every key, which adds overhead for common string and binary dictionaries.
What changes are included in this PR?
Add typed gather implementations for Utf8, LargeUtf8, Binary, and LargeBinary dictionary values. These build the offsets, value buffer, and validity bitmap directly.
Other value types continue to use the generic MutableArrayData implementation. Offset growth is checked so that overflow continues to reach the existing fallback. The dictionary-gather thresholds are unchanged.
Are these changes tested?
I’ve simplified test_gather_dictionary_byte_values to use StringArray, since the string and binary variants share the same gather implementation. The test still covers sliced keys, null keys, and keys that reference null dictionary values.
All 110 arrow-row library tests pass. Workspace tests, workspace Clippy checks, formatting, and spelling checks also pass.
Performance was measured using the existing prepared 8,192-row string-dictionary benchmark on an Intel Xeon E5-2673 v4, Linux x86_64, with Rust 1.99.1. This benchmark cycles through contiguous key chunks.
Two paired Criterion runs used 50 samples, a one-second warmup, and a three-second measurement period. Execution order was reversed for the second pair. The table reports the average of the two runs’ mean estimates.
The 10,000-value control does not enter the dictionary-gather path and averaged 2.5% slower in these measurements. Individual runs varied by several percent.
The additional dictionary sizes were used through a separate local benchmark patch; they are not included in this implementation diff.
Are there any significant user-facing changes?
No public API or intended result changes.
AI assistance: AI tools assisted with implementation, tests, independent review, and validation. I reviewed the code, tests, and description before submission.