Skip to content

feat(btrblocks): allow string dictionaries in the CUDA compression preset - #10917

Merged
0ax1 merged 10 commits into
vortex-data:developfrom
TanishGudise:tg/pr3-cuda-string-dict
Nov 10, 2026
Merged

0ax1 merged 10 commits into
vortex-data:developfrom
TanishGudise:tg/pr3-cuda-string-dict

Conversation

@TanishGudise

Copy link
Copy Markdown
Contributor

Summary

CompressionMode::Cuda excludes StringDictScheme, but execute_dict_varbinview already decodes string dictionaries on the GPU. Keeping the scheme makes GPU-mode files 16.0% smaller across compress-bench's 17-dataset GPU suite (3,351.3 → 2,813.9 MB). Per-dataset gains: CMSprovider 45.7%, Food 52.9%, HashTags 33.2%, both TPC-H l_comment variants 12.2%, Euro2016 10.7%. The issue has the full table.

Changes

  • vortex-btrblocks/src/builder.rs: drops StringDictScheme from the Cuda exclusions and updates the comment. A new test checks that the preset keeps string dictionaries but not binary ones. This is the only change to CPU-compressor code.

Evidence with this branch (A40, CUDA 13.0):

  • --gpu-verify passes on all 17 datasets. develop plus only this line fails 5: taxi, CMSprovider, Euro2016, Food, HashTags.
  • The CPU-fallback census is identical to develop's: 59 constant-array fallbacks, none from dictionaries.
  • The dictionary gather takes at most 6.1% of decode time and reaches 62-85% of the measured 563 GB/s.
  • vortex-cuda lib suite on this branch: 723 passed, 0 failed.
  • compress_gpu fuzz (CI flags, ASAN): 100,000 runs in 256 s, 0 crashes.

Benchmarks

Setup:

  • compress-bench --gpu-decompress --iterations 5, a fresh process per run, taskset -c 2-7, 3 ABBA rounds.
  • Speedup = develop time / branch time.
  • The same-design A/A floor is 0.14 in |log|, so a "faster" claim needs a 95% CI above 1 and a speedup above 1.32×.
Dataset File MB, develop → branch Speedup [95% CI]
Food 84.7 → 39.9 1.55× [1.41, 1.71] faster
CMSprovider 771.0 → 418.5 1.37× [0.66, 2.86] within noise
Euro2016 163.3 → 145.9 1.46× [0.92, 2.32] within noise
TPC-H l_comment chunked 169.6 → 148.8 1.36× [0.55, 3.34] within noise
taxi 52.0 → 51.5 1.28× [0.32, 5.11] within noise
HashTags 235.2 → 157.0 1.22× [0.89, 1.69] within noise
TPC-H l_comment canonical 169.6 → 148.8 1.18× [0.71, 1.96] within noise
Arade 144.2 → 141.7 1.05× [0.42, 2.68] within noise

The nine datasets whose files don't change got one AB/BA round each. Eight came out at 1.01-1.18×, and one (wide table cols=1000 chunks=50) at 0.91×, inside the A/A floor. The measurements are from an A40 (GA102); the repo's GPU benchmarks run on A10G.

API changes

only_cuda_compatible(), and therefore cuda_write_strategy, can now emit string dictionaries.

  • An ArrowDeviceArrayStream with the default DictionaryExport::Preserve rejects a batch whose Arrow schema differs from the first.
  • With this change, 6 of CMSprovider's 14 string columns mix dictionary and constant chunks.
  • Today's Cuda files already do this in numeric columns: 7 of taxi's 18 numeric columns and 5 of CMSprovider's 12.
  • Streams with DictionaryExport::Decode, which the FFI scan uses, are unaffected.

Overlap: #10425 adds a line next to the removed one, so whichever merges second needs a one-line rebase. #9762 changes dictionary selection, so the sizes are as of a59294e.

AI assistance

Developed with Claude Code (Claude Opus 5.5), which wrote most of the code, tests and this description under my direction. I reviewed the diff and the measurements.

🤖 Generated with Claude Code

TanishGudise and others added 4 commits October 27, 2026 13:50
Codes under null positions may hold any value, and so may corrupt input. The CUDA dictionary gathers read values[code] for every row, so such a code read outside the dictionary. Every gather now checks the code against the dictionary length and writes a zero value for a code outside it.

Adds tests for out-of-range codes under nulls for string, primitive, decimal and bool values (with host-resident and device-resident codes), the dictionary the GPU fuzzer crashed on, all-null codes, codes sliced across 1024/2048 boundaries, full-range u16 and beyond-u16 dictionaries, the 12-byte inline boundary, values longer than 4 KiB and non-UTF-8 binary values.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
The GPU fuzz target only generated dictionaries with primitive values. It now also generates dictionaries with UTF-8 or binary values, dictionaries whose nullable codes hold arbitrary values under null positions, and string arrays compressed with the CUDA preset.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
…host

CanonicalCudaExt::into_host executed a child that CUDA execution left encoded on the host, with its buffers still on the device. A run-end encoded validity mask is such a child, and so is a nullable dictionary's validity, a lazy take of the values' validity through the device-resident codes. Both panicked on a host-buffer read: compress-bench --gpu-verify fails this way on CMSprovider and Euro2016 with the current CUDA preset. The child's buffers are now copied to the host before it is executed there. Children that are already canonical take the existing path, the copies run concurrently, and an array's buffers are replaced only when one of them moves.

Adds tests that copy to the host a primitive array whose validity is a run-end or sparse encoded mask on the device, and string and primitive dictionaries with nullable values and device-resident codes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
The CUDA dictionary gather decodes Dict<Utf8> into views over the decoded values, so the CUDA preset no longer needs to exclude StringDictScheme. Binary dictionaries stay excluded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
@codspeed

codspeed Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

2 warnings

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 1916 untouched benchmarks
⏩ 48 skipped benchmarks1


Comparing TanishGudise:tg/pr3-cuda-string-dict (c4dff20) with develop (30446ea)

Open in CodSpeed

Footnotes

  1. 48 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. ↩

@TanishGudise

Copy link
Copy Markdown
Contributor Author

A note on the red checks here, in case it saves you a look:

@joseph-isaacs joseph-isaacs added action/bench-gpu-compress Run only the GPU compression benchmark on this PR action/benchmark-gpu-compress labels Oct 29, 2026
0ax1 and others added 5 commits November 6, 2026 15:35
Replace the `in_dictionary` helper with the comparison it wrapped at its two call sites, so the check reads directly in each kernel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
The string-dictionary and CUDA-preset string generators in the GPU fuzz target aren't part of the bounds fix, so they move to a separate change. The fuzz target keeps the dictionaries with arbitrary codes under null positions, which exercise the bounds check, and the regression test now describes the dictionary the fuzzer found without quoting input bytes that no longer reproduce it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Tanish Gudise <[email protected]>
@0ax1
0ax1 self-requested a review November 10, 2026 11:37
@vortex-data vortex-data deleted a comment from github-actions Bot Nov 10, 2026
@0ax1 0ax1 added the changelog/feature A new feature label Nov 10, 2026
@0ax1
0ax1 enabled auto-merge (squash) November 10, 2026 12:08
@0ax1
0ax1 merged commit 0767966 into vortex-data:develop Nov 10, 2026
98 of 99 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action/bench-gpu-compress Run only the GPU compression benchmark on this PR action/benchmark-gpu-compress changelog/feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants