libavif Thread Pooling for Animated AVIF
This article provides an overview of how the libavif library manages multi-threading and thread pooling when processing animated AVIF sequences. It explains the relationship between libavif and underlying AV1 codecs, details how to configure thread limits during encoding and decoding, and highlights key performance behaviors during parallel frame processing.
The Role of Underlying AV1 Codecs
To understand thread pooling in libavif, it is essential to recognize that libavif itself does not manage a central thread pool for image processing. Instead, libavif acts as a container parser and multiplexer. It parses the ISOBMFF container format of the AVIF file and delegates the actual heavy lifting of encoding or decoding the AV1 video frames to external codecs, such as:
- dav1d (highly optimized CPU decoder)
- aom (the reference encoder/decoder from the Alliance for Open Media)
- rav1e (Rust-based AV1 encoder)
- SVT-AV1 (scalable video technology encoder)
Consequently, thread pooling and CPU core utilization are handled directly by the worker threads spawned by these underlying codecs.
Configuring Threads in libavif
Developers control the concurrency level of animated sequence
processing by configuring the maxThreads property on the
encoder or decoder structures:
- For Decoding: Setting
avifDecoder->maxThreadsdictates how many threads the underlying decoder (likedav1doraom) is permitted to spawn. - For Encoding: Setting
avifEncoder->maxThreadslimits the number of concurrent worker threads the encoder uses to compress the frames.
If maxThreads is set to 1, processing runs
strictly on a single thread. If it is set to a value greater than
1, the underlying codec initializes its internal thread
pool up to that limit to parallelize operations.
Threading During Animated Decodes
Animated AVIF files consist of a sequence of AV1 keyframes and non-keyframes (inter-frames). When decoding these sequences, libavif feeds the frames sequentially or on-demand to the decoder. The underlying decoder uses two primary types of multi-threading:
- Tile-Based Multi-threading: AV1 frames can be divided into independent grid regions called tiles. When tile threading is active, the decoder assigns different tiles of a single frame to different threads in its pool, allowing them to be decoded simultaneously.
- Frame-Based Multi-threading: Decoders like
dav1dcan decode multiple frames in a sequence concurrently if there are no temporal dependencies blocking them. The thread pool manages these dependencies, ensuring that reference frames are sufficiently decoded before dependent frames begin processing.
Threading During Animated Encodes
Encoding animated AVIF sequences is computationally expensive. When
maxThreads is set to a high value, the AV1 encoder utilizes
its thread pool for several parallel operations:
- Row-Based Multi-Threading (RMT): Allows the encoder to process different rows of a frame in parallel once the top-left neighbor blocks are completed.
- Tile-Based Encoding: Like decoding, encoding can split a frame into independent tiles and assign each tile to a separate thread in the pool.
- Pre-analysis and Motion Estimation: Encoders often perform lookahead passes on future frames in the animation sequence to determine optimal compression strategies, utilizing the thread pool to analyze multiple frames simultaneously.
Thread Oversubscription Warning
Because libavif relies on the active codec’s internal threading
mechanisms, each instance of an avifDecoder or
avifEncoder maintains its own separate thread pool.
If an application attempts to decode or encode multiple animated AVIF
files concurrently on different application threads, each instance will
spawn up to its configured maxThreads. This can easily lead
to CPU thread oversubscription, high context-switching overhead, and
degraded performance. To prevent this, applications processing multiple
animations in parallel should lower the maxThreads value
per instance relative to the total number of available CPU cores.