@@ -197,6 +197,56 @@ def test_sharding_partial_read(
197
197
assert np .all (read_data == 1 )
198
198
199
199
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
+
200
250
@pytest .mark .parametrize (
201
251
"array_fixture" ,
202
252
[
0 commit comments