Optimizing FFmpeg for AMD EPYC Processors

High-core-count server processors like AMD EPYC require specific tuning to maximize FFmpeg throughput, as standard single-command execution often suffers from diminishing returns beyond 16 to 32 threads. This guide covers the essential techniques for optimizing FFmpeg on EPYC systems, including mitigating NUMA node latency, overcoming video encoder threading limitations through horizontal scaling and video chunking, and configuring encoder-specific thread parameters.

1. Account for NUMA Architecture

AMD EPYC processors use a chiplet design comprised of multiple Core Complex Dies (CCDs) grouped into Non-Uniform Memory Access (NUMA) domains. If an FFmpeg process running on one CCD constantly accesses memory attached to another, memory bandwidth drops and latency increases.

To resolve this, pin your FFmpeg processes to specific NUMA nodes or CPU core sets using numactl or taskset:

# Run FFmpeg strictly on NUMA node 0 and its local memory
numactl --cpunodebind=0 --membind=0 ffmpeg -i input.mp4 -c:v libx264 -preset slow output.mp4

# Alternatively, pin to specific physical cores (e.g., cores 0-15)
taskset -c 0-15 ffmpeg -i input.mp4 -c:v libx264 output.mp4

In multi-socket EPYC systems, ensure you run distinct FFmpeg workloads per socket to avoid interconnect bottlenecks across the AMD Infinity Fabric.

2. Shift from Vertical to Horizontal Scaling

Most video encoders (such as libx264, libx265, and libsvtav1) hit a parallelization ceiling:

  • libx264: Scalability drops significantly past 16 threads.
  • libx265: Struggles to efficiently utilize more than 32 threads per stream and experiences severe quality/efficiency trade-offs when thread counts are too high.
  • libsvtav1: Performs best between 8 and 16 cores per instance.

Instead of assigning an entire 64-core or 128-core processor to a single FFmpeg task, maximize total system throughput by running multiple concurrent FFmpeg jobs.

Use task spoolers, GNU Parallel, or custom queue workers to divide jobs across the core complexes:

# Encode 8 files simultaneously across an EPYC server using GNU Parallel
parallel -j 8 numactl --cpunodebind={%} --membind={%} \
ffmpeg -i {} -c:v libx265 -crf 23 -threads 8 {.}.mp4 ::: *.mkv

3. Chunk-Based Encoding for Single Large Files

If you must transcode a single large file as fast as possible, do not rely on global FFmpeg threading. Split the video into smaller keyframe-aligned segments, transcode the segments in parallel across the available CPU cores, and concatenate the results.

Tools like Av1an or custom shell scripts can segment the source video into scenes, transcode each scene concurrently across specific NUMA nodes, and stitch them back together without quality loss or frame desynchronization.

4. Fine-Tune Encoder-Specific Thread Parameters

By default, FFmpeg attempts to auto-detect thread counts, which often leads to allocating more threads than an encoder can efficiently handle. Restrict threads manually:

For x264 (libx264)

Explicitly limit the thread pool and frame slicing:

ffmpeg -i input.mp4 -c:v libx264 -threads 16 -x264-params sliced-threads=0 output.mp4

For x265 (libx265)

Use the pools parameter to specify how many threads libx265 can use per NUMA node:

# Limit x265 to 16 threads mapped to the current node
ffmpeg -i input.mp4 -c:v libx265 -x265-params pools=16:frame-threads=4 output.mp4

For SVT-AV1 (libsvtav1)

SVT-AV1 handles modern multi-core architecture better than older encoders. You can set the thread pool with the lp (logical processor) setting:

ffmpeg -i input.mp4 -c:v libsvtav1 -svtav1-params lp=16 output.mp4

5. Prevent I/O Bottlenecks

High-throughput processing across dozens of cores can saturate standard storage pipelines, leaving the CPU cores waiting for data.

  • RAM Disks (tmpfs): For segment-based encoding or high-speed pipelines, write intermediate chunks to memory rather than NVMe or network storage.
  • Fast Decoders: Decoding high-bitrate files can bottleneck the encoder. Use -threads on the input side (ffmpeg -threads 8 -i input.mp4 ...) or hardware-accelerated decode pipelines where applicable to feed raw frames to the CPU rapidly.