dav1d ARM Assembly Optimizations for AV1 Decoding

This article explores how the open-source dav1d decoder delivers industry-leading AV1 video playback speeds on ARM-powered devices, from smartphones to servers. By bypassing compiler auto-vectorization in favor of hand-tuned assembly, dav1d maximizes the architectural features of ARMv7 and AArch64 architectures. The following breakdown examines the key assembly-level techniques employed—including custom ARM NEON SIMD implementations, aggressive register allocation, pipeline-aware instruction scheduling, and specialized algorithms for AV1 in-loop filters.

Hand-Crafted ARM NEON SIMD Vectorization

Compilers often struggle to auto-vectorize complex video decoding routines due to pointer aliasing, non-trivial branching, and boundary conditions. dav1d addresses this by implementing all compute-intensive inner loops directly in hand-written assembly using ARM NEON SIMD (Single Instruction, Multiple Data).

NEON provides 128-bit vector registers capable of processing 16 8-bit integers or 8 16-bit integers per instruction. dav1d leverages this across core decoding tasks:

Register Allocation and Spill Reduction on AArch64

AArch64 provides 31 general-purpose 64-bit registers (x0x30) and 32 128-bit SIMD/floating-point registers (v0v31). dav1d's assembly routines are designed to maximize the occupancy of these registers.

By keeping coefficients, intermediate transform values, filter coefficients, and prediction states entirely within the v0v31 vector registers, dav1d eliminates stack spilling (saving and restoring variables to memory). This drastically cuts down on L1 cache interactions and avoids load-to-use pipeline stalls, which is critical for low-power mobile cores with limited cache bandwidth.

Loop Filter Optimization: CDEF and Loop Restoration

AV1 features three distinct in-loop filtering stages that make decoding computationally heavy: the Deblocking Filter, the Constrained Directional Enhancement Filter (CDEF), and Loop Restoration (Wiener and Self-Guided filters). dav1d implements bespoke assembly strategies for each:

In-Register Matrix Transposition

Inverse transform stages and directional filtering require frequent transpositions of 4x4, 8x8, 16x16, and 32x32 blocks of pixel data. Writing data back to cache to read it column-wise is slow.

dav1d relies on ARM NEON permute and interleave instructions, specifically:

These operations allow entire 8x8 blocks of transform coefficients to be transposed in-place within the vector register file in a handful of cycles.

Instruction Scheduling and Latency Hiding

Modern ARM cores, such as ARM Cortex-A and Apple Silicon, rely on wide out-of-order execution pipelines. dav1d’s assembly is tuned to keep execution units fully saturated:

Efficient Film Grain Synthesis

AV1 uses grain synthesis to simulate film grain at the playback stage, avoiding the bit-cost of encoding noise. dav1d implements the PRNG (Pseudo-Random Number Generator) and Gaussian noise application directly in NEON assembly, vectorizing the calculation of random grain patterns across entire macroblocks to make this post-processing step virtually free on ARM processors.