fix(chunk-grids): make array creation O(1) in per-dimension chunk count - #4218
fix(chunk-grids): make array creation O(1) in per-dimension chunk count#4218d-v-b wants to merge 6 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4218 +/- ##
==========================================
+ Coverage 94.19% 94.20% +0.01%
==========================================
Files 92 92
Lines 12825 12830 +5
==========================================
+ Hits 12080 12087 +7
+ Misses 745 743 -2
🚀 New features to boost your workflow:
|
Merging this PR will improve performance by 11.82%
Performance Changes
Tip Curious why this is faster? Comment Comparing Footnotes
|
…k_grid_metadata The defensive TypeError branch is unreachable through the public API, so exercise it directly with a stub dimension object. This was the only genuinely uncovered patch line in zarr-developers#4218; the other lines codecov flagged came from a partial coverage upload. Assisted-by: ClaudeCode:claude-fable-5
Chunk normalization now returns a ChunkGrid whose uniform dimensions are stored as FixedDimension (size + extent) instead of one array entry per chunk, so create_array(shape=(2**62,), chunks=(1,)) succeeds instantly instead of raising "array is too big", and chunks=(1, 1) on a (2**31, 2**31) array no longer allocates ~17 GB per dimension. Explicit per-chunk lists collapse to FixedDimension when they describe a regular grid; genuinely irregular lists become VaryingDimension. Both variants bind chunk sizes to their extent, so the two forms carry the same invariants. The intermediate ChunksTuple type and as_regular_shape helper are removed; create_chunk_grid_metadata consumes the grid directly and serializes uniform dimensions of mixed rectilinear grids as the spec's bare-int step-size shorthand. Creation-time counterpart of the zarr-developersgh-4174 indexing fix. Assisted-by: ClaudeCode:claude-fable-5
Since normalize_chunks_nd returns ChunkGrid dimensions instead of int64 arrays, is_regular_1d only ever receives plain Python sequences; the vectorized numpy path was unreachable in production code. Remove it and narrow the signatures of is_regular_1d / is_regular_nd to Sequence[int]. Assisted-by: ClaudeCode:claude-fable-5
…k_grid_metadata The defensive TypeError branch is unreachable through the public API, so exercise it directly with a stub dimension object. This was the only genuinely uncovered patch line in zarr-developers#4218; the other lines codecov flagged came from a partial coverage upload. Assisted-by: ClaudeCode:claude-fable-5
…ersion The stateful hypothesis tests convert generated chunk grid metadata back into a create_array chunks= argument. Bare-int dimensions — the spec's step-size shorthand, now produced when a uniform dimension of a mixed grid collapses — were wrapped as single-element lists, turning "repeat to cover the axis" into "exactly one chunk" and failing the sum-to-span check (e.g. chunks=[1] for span 3). Extract the conversion into chunks_param_from_rectilinear, pass bare ints through unchanged, and widen ChunksLike to admit mixed int | sequence per-dimension specs, which the normalizer already accepted. Assisted-by: ClaudeCode:claude-fable-5
a6d27c7 to
72af8cb
Compare
|
🤖 AI text below 🤖 Rebased onto current RebaseThe conflict was with #4257 ("accept numpy integers as chunk sizes"), which landed after this branch was cut and rewrote the same region of
New commit: explicit per-chunk lists stay rectilinear (fixes #4272)The explicit-list branch of Relation to #4290#4290 fixes the same issue on current |
Drop the regular-grid collapse for explicit list input in normalize_chunks_1d: a per-chunk size list now always produces VaryingDimension, even when the sizes are uniform or uniform plus a short tail. Scalar specs (ints, numpy integers, and the -1 sentinel) still produce FixedDimension, which is the path that makes array creation O(1) in per-dimension chunk count; explicit lists are already O(n) in the input, so nothing is lost. The grid kind now follows the input syntax, matching 3.2.x behavior: previously an explicitly rectilinear spec whose edges happened to look regular was silently stored as RegularChunkGridMetadata, which changes resize semantics — a regular grid grows by extending the uniform pattern while a rectilinear grid appends an edge chunk, breaking append-oriented layouts like (168,) * 13 + (24,). This is resolution option 1 from zarr-developersgh-4272. Uniform dimensions of mixed scalar/list specs still serialize as the spec's bare-int step-size shorthand; explicit lists serialize as edge lists. The touched-chunk-keys assertions in the resize regression test follow the approach from zarr-developersgh-4290, which fixes the same issue on main via a requested_rectilinear flag. Fixes zarr-developers#4272 Co-authored-by: Shurong Cao <CAOShurong@users.noreply.github.com> Assisted-by: ClaudeCode:claude-fable-5
72af8cb to
535fa0a
Compare
…me grid The stateful hypothesis tests in CI failed in from_array on a rectilinear source whose edges happen to be uniform (e.g. chunk_shapes=((3,),)): _parse_keep_array_attr and _info guarded data.chunks / data.shards with the runtime ChunkGrid.is_regular, but the runtime grid collapses uniform rectilinear dimensions to FixedDimension as an optimization, while metadata.chunks raises for any array whose stored grid is rectilinear. Before explicit per-chunk lists stopped collapsing at creation, the two notions of regularity always agreed for created arrays, so the mismatch was unreachable. Add _stored_chunk_grid_is_regular, which dispatches on the metadata kind, and use it at the three sites that read .chunks/.shards. A uniform-edged rectilinear source now round-trips through from_array with its grid kind intact. Assisted-by: ClaudeCode:claude-fable-5
|
@maxrjones could you review this when you get a chance? It fixes two issues and so I consider it high priority for our next release |
Summary
this is a claude-authored fix of ##4174. in
ChunkLayout, instead of materializing all chunk specifications, even regular ones, as numpy array, we specifically model regular chunk grid dimensions as a single shape + extent. This meansChunksTuplecan just disappear, and we can re-use our chunk grid abstractions (FixedDimensionandVaryingDimension), which is nice and simple. This avoids o(num chunks) memory problems and avoids some unnecessary abstractions.this is basically what @maxrjones suggested here: #3899 (comment). My instinct to make the API "uniform" came at the cost of an inefficient representation, so I'm pulling back on the commitment to "uniformity" here.
Here's claude's summary:
Chunk normalization now returns a ChunkGrid whose uniform dimensions are stored as FixedDimension (size + extent) instead of one array entry per chunk, so create_array(shape=(262,), chunks=(1,)) succeeds instantly instead of raising "array is too big", and chunks=(1, 1) on a (231, 2**31) array no longer allocates ~17 GB per dimension.
Explicit per-chunk lists collapse to FixedDimension when they describe a regular grid; genuinely irregular lists become VaryingDimension. Both variants bind chunk sizes to their extent, so the two forms carry the same invariants. The intermediate ChunksTuple type and as_regular_shape helper are removed; create_chunk_grid_metadata consumes the grid directly and serializes uniform dimensions of mixed rectilinear grids as the spec's bare-int step-size shorthand.
Creation-time counterpart of the gh-4174 indexing fix.
Assisted-by: ClaudeCode:claude-fable-5
[Describe what this PR changes and why, in your own words.]
For reviewers
Check if this purported simplification is in fact simpler, and confirm that we don't see any unpleasant performance side effects.
Author attestation
TODO
docs/user-guide/*.mdchanges/