Skip to content

Commit ef39891

Browse files
committed
Add performance test of partial shard reads
1 parent 36a1bac commit ef39891

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

tests/test_codecs/test_sharding.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,56 @@ def test_sharding_partial_read(
197197
assert np.all(read_data == 1)
198198

199199

200+
@pytest.mark.slow_hypothesis
201+
@pytest.mark.parametrize("store", ["local"], indirect=["store"])
202+
def test_partial_shard_read_performance(store: Store) -> None:
203+
import json
204+
from itertools import product
205+
from timeit import timeit
206+
from unittest.mock import AsyncMock
207+
208+
# The whole test array is a single shard to keep runtime manageable while
209+
# using a realistic shard size (256 MiB uncompressed, ~115 MiB compressed).
210+
# In practice, the array is likely to be much larger with many shards of this
211+
# rough order of magnitude. There are 512 chunks per shard in this example.
212+
array_shape = (512, 512, 512)
213+
shard_shape = (512, 512, 512) # 256 MiB uncompressed unit16s
214+
chunk_shape = (64, 64, 64) # 512 KiB uncompressed unit16s
215+
dtype = np.uint16
216+
217+
store_mock = AsyncMock(wraps=store, spec=store.__class__)
218+
a = zarr.create_array(
219+
StorePath(store_mock),
220+
shape=array_shape,
221+
chunks=chunk_shape,
222+
shards=shard_shape,
223+
compressors=BloscCodec(cname="zstd"),
224+
dtype=dtype,
225+
fill_value=np.iinfo(dtype).max,
226+
)
227+
# Narrow range of values lets zstd compress to about 1/2 of uncompressed size
228+
a[:] = np.random.default_rng(123).integers(low=0, high=50, size=array_shape, dtype=dtype)
229+
230+
num_calls = 20
231+
experiments = []
232+
for concurrency, statement in product([1, 10, 100], ["a[0, :, :]", "a[:, 0, :]", "a[:, :, 0]"]):
233+
store_mock.reset_mock()
234+
zarr.config.set({"async.concurrency": concurrency})
235+
# Each timeit call accesses a 512x512 slice covering 64 chunks
236+
time = timeit(statement, number=num_calls, globals={"a": a}) / num_calls
237+
experiments.append(
238+
{
239+
"concurrency": concurrency,
240+
"statement": statement,
241+
"time": time,
242+
"store_get_calls": store_mock.get.call_count,
243+
}
244+
)
245+
246+
with open("zarr-python-partial-shard-read-performance-no-coalesce.json", "w") as f:
247+
json.dump(experiments, f)
248+
249+
200250
@pytest.mark.parametrize(
201251
"array_fixture",
202252
[

0 commit comments

Comments
 (0)