Skip to content

Commit 3a9d042

Browse files
maxrjonesdcherian
andcommitted
perf: oindex optimization (zarr-developers#3830)
* oindex single dim optimization * changelog * changelog type * Apply suggestions from code review Co-authored-by: Deepak Cherian <dcherian@users.noreply.github.com> * lint --------- Co-authored-by: Deepak Cherian <dcherian@users.noreply.github.com>
1 parent a5715b9 commit 3a9d042

File tree

2 files changed

+20
-12
lines changed

2 files changed

+20
-12
lines changed

changes/3830.misc.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimize the performance of indexing operations when using an array-like indexer on a single dimension.

src/zarr/core/indexing.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -981,19 +981,26 @@ def __iter__(self) -> Iterator[ChunkProjection]:
981981

982982
# handle advanced indexing arrays orthogonally
983983
if self.is_advanced:
984-
# N.B., numpy doesn't support orthogonal indexing directly as yet,
985-
# so need to work around via np.ix_. Also np.ix_ does not support a
986-
# mixture of arrays and slices or integers, so need to convert slices
987-
# and integers into ranges.
988-
chunk_shape = tuple(
989-
g.chunk_size(p.dim_chunk_ix)
990-
for g, p in zip(self.dim_grids, dim_projections, strict=True)
991-
)
992-
chunk_selection = ix_(chunk_selection, chunk_shape)
984+
# NumPy can handle a single array-indexed dimension directly,
985+
# which preserves full slices and avoids an
986+
# unnecessary advanced-indexing copy. Integer-indexed
987+
# dimensions still need the ix_ path for downstream squeezing.
988+
# Example: we skip `ix_` for array[:, :, [1, 2, 3]]
989+
n_array_dims = sum(isinstance(sel, np.ndarray) for sel in chunk_selection)
990+
991+
if n_array_dims > 1 or self.drop_axes:
992+
# N.B., numpy doesn't support orthogonal indexing directly
993+
# for multiple array-indexed dimensions, so we need to
994+
# convert the orthogonal selection into coordinate arrays.
995+
chunk_shape = tuple(
996+
g.chunk_size(p.dim_chunk_ix)
997+
for g, p in zip(self.dim_grids, dim_projections, strict=True)
998+
)
999+
chunk_selection = ix_(chunk_selection, chunk_shape)
9931000

994-
# special case for non-monotonic indices
995-
if not is_basic_selection(out_selection):
996-
out_selection = ix_(out_selection, self.shape)
1001+
# special case for non-monotonic indices
1002+
if not is_basic_selection(out_selection):
1003+
out_selection = ix_(out_selection, self.shape)
9971004

9981005
is_complete_chunk = all(p.is_complete_chunk for p in dim_projections)
9991006
yield ChunkProjection(chunk_coords, chunk_selection, out_selection, is_complete_chunk)

0 commit comments

Comments
 (0)