Chunk shape decides how much you actually read

3 min read · Updated July 29, 2026

HDF5 and Zarr both store large arrays as a grid of fixed-size chunks. Each chunk is compressed and stored as one unit, which means it is also the smallest thing that can be read. You cannot read half a chunk: to get one element, the library fetches, decompresses and discards the whole chunk it lives in.

Read amplification is the ratio between bytes actually read and bytes wanted. It is entirely determined by the relationship between chunk shape and access pattern, and it is the single largest lever on read performance for both formats.

The same array, two access patterns

Take a 2-D array chunked into wide, short blocks, a common default when data arrives row by row. Reading one row touches a single row of chunks. Reading one column touches every chunk in the array.

shaded = chunks read · outlined = data wanted
Aligned read: one row of chunks fetched, most of it used.
shaded = chunks read · outlined = data wanted
Orthogonal read: the same volume of data requested, but every chunk in the array is fetched and discarded.

Nothing about the array changed. The second read is slower by roughly the number of chunks along the row axis, because that is how many extra chunks it had to decompress to reach the elements it wanted.

ArrayChunk shapeReadBytes wantedBytes readAmplification
10000 × 10000 float3210000 × 1one row40 KB400 MB10000×
10000 × 10000 float3210000 × 1one column40 KB40 KB
10000 × 10000 float32500 × 500one row40 KB20 MB500×
10000 × 10000 float32500 × 500500 × 500 tile1 MB1 MB
Uncompressed float32. Amplification is chunks-touched × chunk-size ÷ bytes-requested.

Picking a chunk shape

  1. Start from the access pattern, not the array. Whatever you slice most often should be contained in as few chunks as possible. If you read time series along the time axis, the time axis should be long in the chunk; if you read spatial snapshots, it should be short.
  2. Target roughly 1 to 10 MB per chunk, uncompressed. Below about 1 MB, per-chunk overhead and metadata dominate, especially on object storage, where each Zarr chunk is a separate request. Above about 10 MB, a small read wastes a lot of decompression.
  3. Prefer chunks that divide the shape evenly. Partial edge chunks are stored at full size in both formats, so a 1000-long axis chunked at 300 wastes most of the fourth chunk.
  4. Check the number of chunks. A 100,000-chunk Zarr store is 100,000 objects to list and fetch; a 100,000-chunk HDF5 file has a correspondingly large B-tree index to traverse.
Python
import h5py, numpy as np

# Row-major reads: keep whole rows together.
with h5py.File("out.h5", "w") as f:
    f.create_dataset("x", shape=(10000, 10000), dtype="f4",
                     chunks=(64, 10000), compression="gzip")

# Zarr v3: the same decision, different spelling.
import zarr
z = zarr.create_array(store="out.zarr", shape=(10000, 10000),
                      chunks=(64, 10000), dtype="f4")
A 64 by 10000 float32 chunk is 2.56 MB uncompressed, inside the useful range.

The HDF5 chunk cache

HDF5 keeps decompressed chunks in a per-dataset cache. As of HDF5 2.0 that cache defaults to 8 MiB with 8191 slots; releases before 2.0 defaulted to 1 MB with 521 slots. A single chunk larger than the cache is never retained, so a loop that revisits it re-reads and re-decompresses it every time, a common cause of "HDF5 got slower when I increased chunk size".

Python
import h5py

# Size the cache to hold the chunks a loop will revisit.
with h5py.File("out.h5", "r", rdcc_nbytes=256 * 1024**2, rdcc_nslots=10007) as f:
    x = f["x"]
    for i in range(x.shape[0]):
        row = x[i, :]
rdcc_nslots is best set to a prime well above the number of chunks that fit in the cache.

Checking what a file already uses

Chunk shape is fixed at creation and cannot be changed in place; rechunking means rewriting. The first step with an unfamiliar file is reading its existing chunk shape: dataset.chunks in h5py, the chunk_grid field of zarr.json in Zarr v3, or the dataset inspector in the HDF5 viewer and Zarr viewer, which show chunk shape and compression alongside the array shape.

Sources

  1. HDF Group: Dataset storage layout and chunking
  2. h5py: Chunked storage
  3. HDF5: H5Pset_chunk_cache (default cache size and slot count)
  4. Zarr: Optimizing performance (chunk size and shape guidance)

Related articles

Inspect these files

ViewKit opens these formats in the browser. The file is parsed locally and never uploaded.

← All articles