Reuse UTF-8 scratch for DataTable dictionary decoding - #19535
Open
xiangfu0 wants to merge 1 commit into
Open
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19535 +/- ##
============================================
- Coverage 67.72% 67.69% -0.03%
Complexity 1450 1450
============================================
Files 3490 3490
Lines 225032 225042 +10
Branches 35527 35530 +3
============================================
- Hits 152393 152346 -47
- Misses 60612 60664 +52
- Partials 12027 12032 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Decoding a string dictionary allocates temporary UTF-8 storage for every entry. Reuse method-local scratch while preserving each returned String's ownership and existing buffer and error behavior.
xiangfu0
force-pushed
the
xiangfu0/java-datatable-dictionary-decode
branch
from
September 12, 2026 19:25
562411d to
a42ffd9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Decoding a V4 DataTable allocates a temporary UTF-8 byte array for each string dictionary entry. Reuse one method-local scratch array, growing to the required length only when necessary. Each returned String owns its contents. Empty entries, wire encoding, the standalone string decoder and malformed-input behavior remain unchanged. The change includes regression tests for buffer state, String ownership and complete V4 messages.
Reproduction and benchmark
Measure
DataTableFactory.getDataTableon already encoded V4 messages, including source-buffer duplication and complete decoding, using heap, direct and read-only heap buffers. Input preparation/encoding, SQL parsing, scans, aggregation, JNI and transport are excluded. Each fork validates all decoded rows, schema, metadata and source-buffer position before timing.Workloads:
NUMERIC_5000has 5,000 INT/LONG rows and no string dictionary;STRING_ONEhas one entry;ASCII_5000has 5,000 unique 64-byte strings;UTF8_5000uses Japanese, supplementary-plane and accented characters;OUTLIER_5000starts with one 262,152-byte string followed by 4,999 64-byte strings;GROWING_512has 512 distinct strings increasing from 32 to 4,120 bytes in 8-byte steps, requiring a larger scratch array for every entry.Measured against upstream
5771d6acea60cd72738116835111cf7c49965373on an Apple M2 Max (12 cores, 32 GiB), macOS ARM64 and Temurin JDK 25. Three alternating AB/BA/AB pairs, one fresh JMH fork per case/arm, one thread, 512 MiB heap, three 500 ms warmups and five 500 ms measurements, with GC profiling. Ordinary shared-host activity remained; no task builds or other task benchmarks ran during timing.Both arms used identical measurement code and a pinned runtime; only the
DataTableImplV4classes differed. Filename-sorted classpaths, runtime manifests and source hashes were checked before/after each arm. This is a controlled class comparison, not a packaged deployment comparison.Results
Times and allocations are medians of three arm means; ratios are medians of paired baseline/candidate times, not ratios of displayed medians. Values above 1 favor the candidate; ranges retain all three pairs.
ASCII/UTF-8 dictionary medians show modest latency gains and 40.8%/18.9% less allocation. Outlier-first dictionaries allocate 26.6% less, but the original heap/read-only median paired slowdowns were about 3.3%/2.7%. Growing-length and single-entry controls also include latency non-wins; numeric, single-entry and growing-length allocation is unchanged. Allocation here means bytes allocated per operation, not peak heap or RSS.
Separate longer outlier follow-up
The original read-only pair 2 was 109.718 → 115.326 µs/op, a +5.111% slowdown, flagged by benchmark review. Its original three-pair median paired slowdown was 2.689%. These findings remain part of the assessment.
Three fresh AB/BA/AB pairs used unchanged source/runtime, five 1-second warmups and ten 1-second measurements across all three outlier buffer types; other settings were unchanged. Source/runtime checks passed before/after each arm. These results are separate from the original protocol and do not replace it.
The longer protocol did not reproduce the >5% regression or explain its cause; one longer heap pair remained slower. The allocation reduction repeated. The original tradeoff remains: these shared-host component measurements establish neither universal latency improvement, query throughput, production capacity nor 10× performance.
Validation
114 tests passed:
DataTableStringDictionaryTest(48),DataTableDictionarySerDeTest(8),DataTableSerDeTest(9),SelectionOperatorServiceTest(5) andInterSegmentGroupBySingleValueQueriesTest(44). Coverage includes empty entries/dictionaries, Unicode, malformed UTF-8 replacement, negative counts/lengths, truncation, buffer position/limit/mark, independence of earlier Strings and complete V4 scalar/MV/null/metadata/exception messages across heap/direct, read-only and sliced buffers. Independent correctness and testing source reviews found no actionable issues; the benchmark review's original slowdown remains disclosed above.Recorded local checks passed: normal compilation/tests, packaging, Spotless, Checkstyle, license format/check and Apache RAT. Scoped lint retained the baseline’s two constructor this-escape warnings and four dependency-manifest path warnings, with no new source warnings; it excluded the ASF-header dangling-doc category. A prior same-base full-reactor Xlint run failed in unchanged Zstandard code on a missing JetBrains annotation and was not repeated.