feat: subchunk write order#3826
Conversation
| end = "end" | ||
|
|
||
|
|
||
| class SubchunkWriteOrder(Enum): |
There was a problem hiding this comment.
advantage of an enum over Literal["morton", "unordered", "lexicographic", "colexicographic"]?
There was a problem hiding this comment.
Just copied what was done for ShardingCodecIndexLocation!
There was a problem hiding this comment.
I'm not a huge fan of enums in python (including ShardingCodecIndexingLocation), so unless you object I think it would be better to use a simple Literal + a final tuple of strings, like:
SubchunkWriteOrder = Literal["morton", "unordered", "lexicographic", "colexicographic"]
SUBCHUNK_WRITE_ORDER: Final[tuple[str, str, str, str]] = ("morton", "unordered", "lexicographic", "colexicographic")
There was a problem hiding this comment.
Done (hopefully)!
Co-authored-by: Davis Bennett <davis.v.bennett@gmail.com>
|
|
||
| if self._is_complete_shard_write(indexer, chunks_per_shard): | ||
| shard_dict = dict.fromkeys(morton_order_iter(chunks_per_shard)) | ||
| shard_dict = dict.fromkeys(np.ndindex(chunks_per_shard)) |
There was a problem hiding this comment.
cc @mkitti
Here and below, I don't think there is any need to construct the dict in morton order, right? There should be no correctness or performance hit here?
@d-v-b This now ensures we only shuffle in the unordered case once so the test is nice and clean - write once + get order, create a new codec with the same seed + create the iterator from that codec, match orders
There was a problem hiding this comment.
In Python, dicts are ordered and I think the optimal iteration order may need to be encoded in the dict the last time I examined the situation. I was just trying to preserve the situation before my edits.
There was a problem hiding this comment.
So this wasn't about dictionary order, but instead in the vectorized case, the order to ShardReader.to_dict_vectorized had to match that of what ShardReader was internally generating, as it turned out morton order. So I'm glad I caught this because I think it means the data was being corrupted for the other orders (which weren't getting hypothesis-tested).
So I'm going to add something to the hyptothesis tests for this.
I had the same feeling initially that the dictionary order mattered, but it turns out the final call to _encode_shard_dict actually handles the ordering for us to the output buffer while writing to the intermediate shard_dict can be done in any order, as long as the final buffer is done in the correct order
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3826 +/- ##
==========================================
+ Coverage 93.39% 93.43% +0.04%
==========================================
Files 88 88
Lines 11839 11874 +35
==========================================
+ Hits 11057 11095 +38
+ Misses 782 779 -3
🚀 New features to boost your workflow:
|
d-v-b
left a comment
There was a problem hiding this comment.
this looks good, thanks!
we have some interesting follow-up work:
- finding a nice way to expose the sharding codec settings in the signature of
create_array - writing a chunk writing routine that can incrementally stream out a shard
- formalizing the distinction between codec attributes that get serialized to JSON and runtime-only attributes
…#3826, partial-read opt zarr-developers#3004, _ShardIndex refactor zarr-developers#3975) Resolves conflicts in sharding.py (kept FusedCodecPipeline sync methods + main's _subchunk_order_iter / _load_partial_shard_maybe; fixed _ShardIndex construction to main's 2-arg signature), array.py (took main's cached regular_chunk_spec), test_codec_pipeline.py (kept the dual-pipeline suite + main's evolve test), .gitignore (union). 423 codec/sharding/parity + 807 codecs/indexing tests pass under both pipelines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…al reads Three integration gaps surfaced when the Fused pipeline met main's new subchunk_write_order (zarr-developers#3826), partial-read coalescing (zarr-developers#3004), and _ShardIndex refactor. Under Fused these caused 25 sharding/parity failures (data was correct in the partial-read cases; the failures were write-order layout + IO-pattern divergence). Fixes: 1. Write order: _encode_shard_dict_sync laid out chunks in hardcoded morton order, ignoring subchunk_write_order. Now iterates _subchunk_order_iter(self.subchunk_write_order), matching the async _encode_shard_dict. Fixes lexicographic/colexicographic/unordered storage. 2. Coalesced sync partial reads: add Store.get_ranges_sync (a synchronous, coalescing counterpart of get_ranges, reusing coalesce_ranges) and ShardingCodec._load_partial_shard_maybe_sync; route _decode_partial_sync's partial branch through it. Sync stores now get zarr-developers#3004's byte-range coalescing without an event loop (fewer, merged reads). 3. Non-sync fallback: FusedCodecPipeline.read now routes non-sync stores (e.g. ZipStore) through the async partial-decode path when the AB codec supports it, instead of _async_read_fallback's whole-shard get(). Matches Batched's IO behavior; avoids over-reading whole shards on partial reads. Tests: the zarr-developers#3004 partial-read tests are made pipeline-aware (assert the active method family: get/get_ranges vs get_sync/get_ranges_sync, gated on store sync support). 573 sharding+parity+pipeline+indexing and 657 codec tests pass under BOTH pipelines (was 25 failing under Fused). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: subchunk write order (#3826) * feat: subchunk write order * chore: export `SubchunkWriteOrder` * chore: docs * chore: relnote * rename * refactor: no enums * Update docs/user-guide/performance.md Co-authored-by: Davis Bennett <davis.v.bennett@gmail.com> * feat: deterministic but random order * fix: make vectorized fetching less reliant on matching order * chore: add hypothesis * refactor: dead code * refactor: more cleanup * don't shard unless there is something to shard * fix: dont mix chunk grid and sharding --------- Co-authored-by: Davis Bennett <davis.v.bennett@gmail.com> (cherry picked from commit 093a153) * refactor: unordered subchunk order means no-promise, not random Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: pin subchunk_write_order survival through pickle Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: remove rng from ShardingCodec; carry subchunk_write_order through pickle Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: describe unordered subchunk order as no-guarantee, drop rng Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: drive sharding strategy through serializer to exercise subchunk_write_order The hypothesis arrays() strategy passed both shards= and a ShardingCodec serializer, which nested the codecs and left subchunk_write_order governing only a 1-element inner grid. Drive sharding through the serializer alone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * harden: guard _subchunk_order_iter; document write-order is not persisted Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * cleanup: use np.ndindex for immaterial intermediate order; drop stale FIXME Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * polish: guard scalar arrays in sharding strategy; align doc value ordering Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert strategies.py changes — unrelated to this PR These changes were patching a latent bug in `arrays()` where ShardingCodec-as-serializer was being double-stacked with `shards=...`, producing nested sharding. Splitting to a follow-up PR so this one stays focused on removing the `rng`/random-subchunk-order surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ilan Gold <ilanbassgold@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In order to encourage ecosystem compatibility + reserve runtime setting strings/enums (see zarrs/zarrs-python#160), subchunk write order is expanded from
mortonto includelexicographic,colexicographic, andunordered(which is randomized).TODO:
docs/user-guide/*.mdchanges/