Repository navigation
perf(arrow-row): avoid encoding oversized dictionary values - #12043
Conversation
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (65afa03) to b231cf1 (merge-base) diff Run configurationrun benchmark row_format
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 rich-T-kid/row-dictionary-sparse-encode (65afa03) to b231cf1 (merge-base) diff Run configurationrun benchmark row_format
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-r5b1bsxo-0e1bca2a-8d8c 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 |
Add an 8,192-row cardinality sweep so sparse dictionary encoding can be compared at the measured crossover and at larger retained value counts.
RowConverter currently encodes every dictionary value, even when a batch references only a small subset. Gather one value per row for dictionaries with at least 32K values and four times more values than rows, avoiding the large values-array cost without penalizing ordinary dictionaries. The cutoff is based on an 8,192-row cardinality sweep: the gathered path crosses over between 20K and 24.6K values, so 32K is deliberately conservative.
65afa03 to
1252049
Compare
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing 1252049 (1252049) to 1cb7eaa diff Run configurationrun benchmark row_format
baseline:
ref: 1cb7eaaf56ada718e4ca2774c054561974f5ec53
changed:
ref: 1252049ea59fe98a10fd1498c2da14fb10dace48
env:
BENCH_FILTER: string_dictionary_sparse_keys
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 1252049 (1252049) to 1cb7eaa diff Run configurationrun benchmark row_format
baseline:
ref: 1cb7eaaf56ada718e4ca2774c054561974f5ec53
changed:
ref: 1252049ea59fe98a10fd1498c2da14fb10dace48
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-1sqo6ofk-2def7ee8-zlmg 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 |
The focused cases were only needed to establish the cutoff and capture hosted evidence. Remove them from the permanent suite and document why the gather path is guarded.
|
@jayzhan211 this is ready for review so please feel free to take a look |
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (71401d6) to b65bab9 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
Results will be posted when all workers finish. File an issue against this benchmark runner |
|
run benchmark row_format |
|
run benchmark row_format |
1 similar comment
|
run benchmark row_format |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (71401d6) to b65bab9 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
Results will be posted when all workers finish. File an issue against this benchmark runner |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (601d0ed) to 258b0d2 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
Results will be posted when all workers finish. File an issue against this benchmark runner |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (601d0ed) to 258b0d2 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
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 rich-T-kid/row-dictionary-sparse-encode (71401d6) to b65bab9 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-xzxr7f8s-1e582ac0-vtcl 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 rich-T-kid/row-dictionary-sparse-encode (71401d6) to b65bab9 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-1rm0t8tn-70a49e36-pc7t 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 rich-T-kid/row-dictionary-sparse-encode (601d0ed) to 258b0d2 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-fcu1jron-5132aa10-mvjb 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 rich-T-kid/row-dictionary-sparse-encode (601d0ed) to 258b0d2 (merge-base) diff Run configurationrun benchmark row_format
env:
BENCH_FILTER: string_dictionary_sparse_keys
shards: 1
DetailsPer-runner informationrow_format — shard 1/1Node: gk3-benchmark-cluster-nap-19bflw4d-2c0bb708-qjd6 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 |
|
run benchmark row_format |
|
run benchmark row_format |
|
Hi @Rich-T-kid, your benchmark configuration could not be parsed (#12043 (comment)). Error: Usage: Any benchmark name is accepted: Per-side configuration ( env:
# shared env is inherited by BOTH the build and the run, so build
# flags go here. Builds default to no debuginfo for speed; opt back
# in for hung-job gdb dumps and cap jobs to stay within memory:
CARGO_PROFILE_RELEASE_DEBUG: "1"
CARGO_BUILD_JOBS: "1"
baseline:
ref: v45.0.0
env:
# per-side env only reaches the benchmark run, not the build
DATAFUSION_RUNTIME_MEMORY_LIMIT: 1G
changed:
ref: v46.0.0
env:
DATAFUSION_RUNTIME_MEMORY_LIMIT: 2GPod size and CPU architecture (each key is optional and keeps the default when absent; requests and limits are set equal): resources:
cpu: "16"
memory: "128Gi"
arch: arm64 # arm64 or amd64Independent benchmark shards (optional; 1–8, default 1): run benchmark arrow_writer
shards: 4Each shard builds both revisions, measures its assigned cases, and posts its own report. Resources and the job deadline apply per shard. DataFusion requests specifying a shard count greater than 1 fail with an error. Omit shards or use shards: 1 for DataFusion. File an issue against this benchmark runner |
|
🤖 Benchmark starting (GKE) | trigger Target: row_format Sharding factor: 1 (1 workers). Comparing rich-T-kid/row-dictionary-sparse-encode (9925688) to b010f53 (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 rich-T-kid/row-dictionary-sparse-encode (9925688) to b010f53 (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-rw3tiycv-5f790bb7-xtb8 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 |
Jefffrey
left a comment
There was a problem hiding this comment.
looks good to me, some minor comments
fyi @jayzhan211
yes I agree that it would make it simpler here but I don't think we should introduce the dependency. I think we should try to optimize this natively and if the results are still not good enough we can consider it. |
Jefffrey
left a comment
There was a problem hiding this comment.
should be good, unless jay wants to take a look
|
thanks @Jefffrey |
Why
Closes #11901.
The benchmark coverage for this change is in the consolidated benchmark PR #12054.
RowConvertercurrently encodes every value in a dictionary before converting a batch. For an 8,192-row slice backed by a large dictionary, the work therefore scales with the retained values array rather than the rows being converted.This is a smaller follow-up to #11904. It avoids the hash map and per-row remap used there, and uses the benchmark crossover requested in review to keep small and dense dictionaries on the existing path.
What changed
When a dictionary has at least 32K values and its values array is at least four times longer than the input, gather one value per row and encode that row-aligned array. All other dictionaries keep the existing dictionary-key lookup path.
The benchmark sweep put the crossover between 20K and 24.6K values. The 32K / 4x guard is deliberately conservative.
Results
The hosted exact-ref run is flat at 10K, 1.3x faster at 32K, 3.45x at 100K, and about 33x at 1M. Row decoding is unchanged, and peak memory drops from 56.8 MiB to 29.5 MiB.
Test coverage
One test checks row equality and round-trip decoding for a sliced dictionary with repeated high keys and nulls.