Skip to content

Commit a53d51f

Browse files
janickmd-v-b
andauthored
fix: avoid copying 0-d chunk data when indexing CPU NDBuffers (#4466)
Co-authored-by: Davis Bennett <davis.v.bennett@gmail.com>
1 parent 25d390b commit a53d51f

3 files changed

Lines changed: 14 additions & 0 deletions

File tree

‎changes/4464.bugfix.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed slow reads of 0-d arrays with large fixed-length string dtypes (e.g. `|S1000000`) caused by copying the chunk data multiple times.

‎src/zarr/core/buffer/cpu.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ def as_numpy_array(self) -> npt.NDArray[Any]:
220220
return np.asanyarray(self._data)
221221

222222
def __getitem__(self, key: Any) -> Self:
223+
# a trailing Ellipsis makes numpy return a 0-d view instead of a scalar, which
224+
# np.asanyarray would copy (slow for large fixed-length string dtypes)
225+
if isinstance(key, tuple) and not any(k is Ellipsis for k in key):
226+
key = (*key, Ellipsis)
223227
return self.__class__(np.asanyarray(self._data.__getitem__(key)))
224228

225229
def __setitem__(self, key: Any, value: Any) -> None:

‎tests/test_buffer.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@ def test_gpu_buffer_prototype() -> None:
198198
ndbuffer.as_scalar()
199199

200200

201+
@pytest.mark.parametrize(("shape", "key"), [((), ()), ((3, 4), (1, 2))])
202+
def test_cpu_ndbuffer_getitem_returns_view(shape: tuple[int, ...], key: tuple[int, ...]) -> None:
203+
# https://github.com/zarr-developers/zarr-python/issues/4464
204+
data = np.full(shape, b"x" * 10)
205+
item = cpu.NDBuffer.from_numpy_array(data)[key].as_numpy_array()
206+
assert item.shape == ()
207+
assert np.shares_memory(item, data)
208+
209+
201210
# TODO: the same test for other buffer classes
202211
def test_cpu_buffer_as_scalar() -> None:
203212
buf = cpu.buffer_prototype.nd_buffer.create(shape=(), dtype="int64")

0 commit comments

Comments
 (0)