Repository navigation
Conversation
The Parquet writer keeps dictionary encoding until the dictionary reaches dictionary_pagesize_limit, even when the dictionary saves nothing. On high-cardinality columns this dictionary-encodes about half of each column chunk (a 2 MiB dictionary of 16-byte strings holds about 105K values), costing CPU and about 10% more bytes than PLAIN. parquet-java decides on the first data page of each column chunk: it keeps the dictionary only if the page's encoded indices plus the dictionary are smaller than the page's values in PLAIN encoding. WriterProperties::Builder::enable_dictionary_benefit_check() applies that rule. When the first page fails it, the writer falls back to PLAIN through the existing FallbackToPlainEncoding(), so the dictionary page and the first page stay dictionary-encoded. The check is off by default. A column chunk that ends before its first page is cut, and values written as an Arrow DictionaryArray, are not checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bodduv
marked this pull request as draft
October 13, 2026 19:38
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.
What
Adds
WriterProperties::Builder::enable_dictionary_benefit_check()(default off). When enabled, the first data page of each dictionary-encoded column chunk decides whether dictionary encoding is kept, using parquet-java's rule (FallbackValuesWriter/DictionaryValuesWriter.isCompressionSatisfying):If the first page fails the check, the writer falls back through the existing
FallbackToPlainEncoding(): the dictionary page and the first page stay dictionary-encoded, and the remaining pages are PLAIN. Without the check (today's behaviour), the dictionary is only abandoned when it reachesdictionary_pagesize_limit.Why
Dremio's Arrow C++ Parquet writer (DX-124448, dremio/dremio#29048) was 0.86× of parquet-java on a CrowdStrike-style CTAS with 60 high-cardinality 16-character string columns. Footer and page dumps showed Arrow C++ dictionary-encoding each column chunk until the 2 MiB limit (about 105K values, roughly half a 211K-row chunk) before falling back. That cost +10.6% bytes per row and the hashing and index-encoding CPU. parquet-java abandons the dictionary after the first page.
How
properties.h: the new builder option and its getter, copied inBuilder(const WriterProperties&).column_writer.cc:ColumnWriterImpl::AddDataPagerecords the exact encoded values size of each page.TypedColumnWriterImplaccumulates the first page's PLAIN size while the check is pending. That is non-null count × width; for BYTE_ARRAY it is count × 4 plus the encoder's existing unencoded-byte counter, and for FLBA count ×type_length. The accounting matchesdict_encoded_size().CommitWriteAndCheckPageLimit.dictionary_pagesize_limitstill applies after the check passes.Known limits
Close().DictionaryArray(WriteArrowDictionary) are not checked, because their PLAIN size is unknown.Tests
column_writer_test.cc:DictionaryBenefitCheckFallsBackAfterFirstPage: unique values give exactly 1 dictionary-encoded data page, and the rest are PLAIN.DictionaryBenefitCheckKeepsBeneficialDictionary: 10 distinct values give all pages dictionary-encoded.properties_test.cc: the default is off; the setters work and survive the builder copy.parquet-internals-test(TestWriterProperties.*), the fullparquet-writer-test(330 tests), andparquet-arrow-reader-writer-test*Dictionar*. All passed.🤖 Generated with Claude Code