Repository navigation
feat(ipc): Skip unchanged dictionaries in ArrowIpcWriterWriteArrayViewWithDictionaries - #979
Draft
rustyconover wants to merge 12 commits into
Draft
rustyconover wants to merge 12 commits into
rustyconover wants to merge 12 commits into
Conversation
ArrowIpcEncoderSetMessageMetadata() was ignored by ArrowIpcEncoderEncodeSimpleDictionaryBatch(); encode it like the schema and record batch messages do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The IPC Field for a dictionary-encoded column describes the dictionary value type, so its children must come from schema->dictionary rather than from the index schema. This replaces the ENOTSUP for dictionary values with children (e.g., struct dictionaries whose children are themselves dictionary-encoded). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add ArrowIpcEncoderSetDictionaryReplacement() to declare the DICTIONARY_REPLACEMENT feature in an encoded Schema message. The writer enables it in stream mode, where a dictionary ID may be written more than once, and the reader no longer rejects streams that declare it (the decoder already replaces dictionaries). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On main, a file could contain exactly one DictionaryBatch, so a schema with more than one dictionary-encoded field couldn't be written, and the footer schema couldn't resolve its dictionary IDs when finalizing the file. Record the dictionary encodings of the footer schema and track the dictionary IDs written to the file: a second batch for the same ID returns EINVAL because IPC files don't support dictionary replacement (deltas remain unsupported when writing files). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When dictionary values contain dictionary-encoded children, a reader needs the nested dictionaries before it can decode the DictionaryBatch that references them. Keep assigning IDs in schema order but write each dictionary after the dictionaries nested in its values. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ArrowIpcWriterWriteArrayStream() wrote every dictionary before every record batch. Skip a dictionary when it has the same length, offset, null count, and buffer addresses (recursively) as the corresponding dictionary of the previous array, which is the case when a producer shares one dictionary among the arrays of a stream (e.g., the nanoarrow IPC reader). Comparing addresses is only safe while both arrays are alive, so the writer keeps the previous array (if it has dictionaries) until the next one is written. ArrowIpcWriterWriteDictionaryBatch() only sees a borrowed view that it can't keep alive and always writes the dictionary. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Write the integration files back using ArrowIpcWriterWriteArrayStream(), which writes dictionaries (writing the arrays with ArrowIpcWriterWriteArrayView() alone does not), and enable the write roundtrip for the dictionary, nested dictionary, shared dictionary, and extension (whose dict-extension column is dictionary-encoded) files. Because the reader shares dictionaries among the batches it decodes, these roundtrips also exercise skipping unchanged dictionaries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… a repeated file dictionary ID Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #979 +/- ##
==========================================
+ Coverage 78.45% 78.59% +0.13%
==========================================
Files 106 106
Lines 17064 17255 +191
Branches 2021 2087 +66
==========================================
+ Hits 13388 13562 +174
+ Misses 2432 2423 -9
- Partials 1244 1270 +26 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…message-metadata # Conflicts: # src/nanoarrow/ipc/writer.c
rustyconover
force-pushed
the
feat/ipc-writer-skip-unchanged-view-dictionaries
branch
from
October 20, 2026 03:48
8c765f2 to
3aa0b89
Compare
…message-metadata # Conflicts: # src/nanoarrow/ipc/writer.c # src/nanoarrow/ipc/writer_test.cc
…wWithDictionaries ArrowIpcWriterWriteArrayViewWithDictionaries() wrote every dictionary before every RecordBatch: its input is borrowed, so it couldn't compare a dictionary with the previous batch's by address as ArrowIpcWriterWriteArrayStream() does. The writer now keeps a copy of each dictionary it writes and compares the next batch's values with it, writing only dictionaries that changed (and those whose values contain a nested dictionary that was written). In a file, batches whose dictionaries are unchanged can now be written this way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
rustyconover
force-pushed
the
feat/ipc-writer-skip-unchanged-view-dictionaries
branch
from
October 20, 2026 22:34
3aa0b89 to
57c4810
Compare
This branch has not been deployed
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.
ArrowIpcWriterWriteArrayViewWithDictionaries()(#968) writes every dictionary before every RecordBatch. Its input is borrowed, so it can't do whatArrowIpcWriterWriteArrayStream()does (#928): keep the previous array alive and compare dictionaries by buffer address. A producer that writes batches one at a time with the same dictionary therefore sends that dictionary again with every batch. In a file, where a dictionary ID may be written only once, it can't write a second batch with dictionaries at all.The writer now keeps a copy of each dictionary this function writes, keyed by dictionary ID, and compares the next batch's values with it:
ArrowArrayViewCompare(..., NANOARROW_COMPARE_IDENTICAL, ...)). This is the common case, e.g. when the producer builds each batch's dictionary the same way, and it needs no copy.ArrowArrayAppendStorageFromArrayView()doesn't support dense unions) are written every time, as before.ArrowIpcWriterWriteDictionaryBatch()drops the saved copy for its ID, so a dictionary written that way is never skipped by mistake, andArrowIpcWriterWriteSchema()drops all of them.ArrowIpcWriterWriteArrayStream()is unchanged. The function's documentation now describes this, including the cost: a copy of each dictionary that is written.Cost: 1000 batches of 2048 rows,
dictionary<int32, string>with 10,000 values, written to a memory buffer (Release, Linux aarch64; best of 3):A dictionary that changes on every batch costs a copy per batch. The follow-up, #980, writes a dictionary that only grew as a delta, which brings that row to 346 ms and 9.2 MB.
Tests (
writer_test.cc):WriteArrayViewWithDictionariesSkipsUnchanged: separately built arrays with dictionaries[foo, bar], [foo, bar], [foo, baz], [foo, baz]write two DictionaryBatches. Read back with nanoarrow and Arrow C++.WriteArrayViewWithDictionariesComparesValues: anint64dictionary[1, null, 3], then the same values as a slice of a longer array with an unknown null count and padding bits set, write one DictionaryBatch.WriteArrayViewWithDictionariesNested: when only a nested dictionary changes, both it and the dictionary that refers to it are written (IDs1, 0, 1, 0over three batches).WriteArrayViewWithDictionariesAfterWriteDictionaryBatch: a dictionary written withArrowIpcWriterWriteDictionaryBatch()in between isn't mistaken for the saved copy.WriteArrayViewWithDictionariesUncopyableValues: a dense union dictionary is written for every batch.WriteArrayViewWithDictionariesInFilenow writes a second, unchanged batch to the file, checks that a changed dictionary returns ENOTSUP, and reads the file back with Arrow C++.Against #968's
writer.c, the first three new tests and the updated file test fail.ctestpasses (Linux aarch64, Debug, 878 tests withNANOARROW_BUILD_TESTS_WITH_ARROW), and 403 tests under ASan/UBSan with clang. The writer tests are clean under valgrind. The changed files are formatted with clang-format 16.0.6, the pre-commit version.This PR's code and description were written by AI (Claude Code) and reviewed by a human maintainer.
🤖 Generated with Claude Code