blosc2.set_prefetch

blosc2.set_prefetch(nthreads: int, max_ahead: int = 0, piece_size: int = 0) → int[source]

Read ahead when slicing arrays and super-chunks stored as contiguous frames on disk.

A slice of an on-disk array reads the blocks it needs one after the other, each read at most as large as the kernel’s read-ahead window. On a network file system every one of those reads is a round trip, so a cold slice can wait for hundreds of them in a row. With read-ahead, the chunks a slice needs (and that are not in the page cache yet) are read by nthreads background threads in pieces of at most piece_size bytes, in the order the slice decompresses them and at most max_ahead bytes ahead of it, and each chunk is decompressed as soon as it is in. Of a chunk that a slice (a[...], NDArray.slice()) covers only in part, only the blocks it needs are read (after the chunk’s header). The data goes to the page cache, so mmap_mode="r" stays zero-copy, and cached pages are not read again.

It applies to NDArray slices (a[...], NDArray.slice()), orthogonal selections (a[[1, 5, 9], ...], NDArray.get_oselection_numpy()) and SChunk slices (schunk[a:b], SChunk.get_slice()).

Which settings to use: reads that are not cached get faster on any storage, most on high-latency ones. The threads mostly wait for the storage, but the kernel spends CPU on every byte they read, and more per byte the more threads read one file at once.

  • 4 threads with the default piece size give most of the speed, on network file systems (NFS, SMB, …) and on local disks; more threads help a little more on network storage and cost more CPU.

  • On local disks, fewer threads (1-2) or larger pieces (4-8 MiB) cost less CPU, for some of the speed (how much depends on the disk).

  • Keep 4 also with many reader processes or threads: in tests on NFS with 24 to 96 reader processes on 64 hardware threads, 4 threads were the fastest setting or within 10% of it. Only when the readers keep all cores busy can fewer threads, or none, be faster.

How many threads run: each slice being read gets up to nthreads read-ahead threads, and slices that threads of the process read at the same time get theirs each. So a process runs up to nthreads times the number of slices it reads at once, at most 256 in all: a program that reads with one thread has nthreads of them, one that reads with 8 threads at once (each with its own handle) up to 8 x nthreads, and 8 processes with one reader each have nthreads each. Decompression threads (nthreads in dparams, blosc2.nthreads) are separate and do not count: they decompress a chunk while the read-ahead threads fetch the next ones, so readers can keep one decompression thread each and still read ahead.

Parameters:
  • nthreads (int) – Number of read-ahead threads (at most 256); 0 turns read-ahead off (the default).

  • max_ahead (int) – Most bytes read ahead of the chunk being decompressed; 0 for the default (64 MiB).

  • piece_size (int) – Largest read handed to one thread, in bytes; 0 for the default (1 MiB), else at least 64 KiB. Each thread keeps a buffer as large as the largest piece it has read.

Returns:

out – The previous number of read-ahead threads.

Return type:

int

Notes

The setting and the threads are per process; a process created with fork() starts its own threads on first use. Read-ahead threads are started as needed and keep running, each with its buffer. The BLOSC_PREFETCH environment variable, set to a number of threads, turns read-ahead on when blosc2 is imported. It works with the default and the memory-mapped file I/O on POSIX systems (Linux, macOS); on Windows it has no effect. Skipping cached pages asks the kernel (cachestat() on Linux >= 6.5, else mincore()), which on Linux answers only for files the process owns or may write to (before 6.5: only files it owns); other files are read again even if cached, which is correct but costs a copy.

Examples

>>> old = blosc2.set_prefetch(4)  # network storage: 4 threads, 1 MiB pieces, up to 64 MiB ahead
>>> _ = blosc2.set_prefetch(2, piece_size=8 << 20)  # less CPU: 2 threads, 8 MiB pieces
>>> _ = blosc2.set_prefetch(old)