Optimize FFmpeg for AMD EPYC Multi Core CPUs
High-core-count processors like AMD EPYC offer massive computational power, but standard FFmpeg configurations often fail to utilize all available cores efficiently. This guide explains how to optimize FFmpeg for multi-core server CPUs by configuring thread settings, leveraging NUMA node pinning, using parallel execution, and fine-tuning codec-specific parameters to maximize encoding throughput.
1. Understand Threading Limits in Codecs
Most video encoders have upper limits on how many threads they can
efficiently scale across. For example, libx264 and
libx265 exhibit diminishing returns or even performance
degradation when assigned more than 16 to 22 threads for a single 1080p
or 4K encode.
If you run a single FFmpeg command on a 64-core AMD EPYC processor, the CPU utilization will likely remain low. To combat this, you must control the threading behavior manually:
-threads: Limits the global number of threads FFmpeg uses for decoding and filtering.-filter_threads: Limits threads allocated specifically for video filters.
Example of limiting a single process to 16 threads:
ffmpeg -i input.mp4 -c:v libx265 -threads 16 -preset medium output.mp42. Leverage Parallel Processing (Multi-Instance Encoding)
The most effective way to utilize a 64-core or 128-core EPYC processor is to run multiple FFmpeg instances simultaneously rather than trying to force one video to use all cores.
Using GNU Parallel
You can batch process multiple videos concurrently, assigning a set number of threads to each instance.
find . -name "*.mp4" | parallel -j 4 ffmpeg -i {} -c:v libx264 -threads 8 outputs/{/.}.mkvIn this example, -j 4 runs 4 parallel FFmpeg jobs,
and each job is limited to 8 threads, utilizing 32 threads in
total.
Segment-Based Encoding (Split and Merge)
For single large files, use tools like Chunked Encoding to split the video into segments, encode the segments in parallel across your EPYC cores, and concatenate them back together.
3. Optimize for NUMA Architecture
AMD EPYC processors use a Multi-Chip Module (MCM) design, split into multiple Non-Uniform Memory Access (NUMA) nodes. If an FFmpeg thread running on NUMA node 0 tries to access memory allocated on NUMA node 3, it introduces latency.
To prevent performance loss from inter-socket and inter-node memory transfer:
- Pin FFmpeg to specific NUMA nodes using
numactlon Linux. - Bind CPU cores directly to prevent the OS scheduler from bouncing threads across the CPU.
Launch FFmpeg bound to NUMA node 0 and its local memory:
numactl --cpunodebind=0 --membind=0 ffmpeg -i input.mp4 -c:v libx265 -threads 16 output.mp4If running parallel jobs, distribute them across different NUMA nodes:
# Run Job 1 on Node 0
numactl --cpunodebind=0 --membind=0 ffmpeg -i vid1.mp4 -c:v libx265 output1.mp4 &
# Run Job 2 on Node 1
numactl --cpunodebind=1 --membind=1 ffmpeg -i vid2.mp4 -c:v libx265 output2.mp4 &4. Codec-Specific Threading Parameters
For maximum efficiency, pass threading parameters directly to the
underlying libraries (libx264, libx265,
libvpx-vp9, libsvtav1).
libx265 (HEVC)
libx265 is highly sensitive to NUMA pools. Use the
pools parameter to restrict the encoder to specific CPU
sockets or core complexes.
ffmpeg -i input.mp4 -c:v libx265 -x265-params pools=16 output.mp4This restricts libx265 to a pool of 16 threads,
preventing it from spawning threads across the entire EPYC
socket.
SVT-AV1 (AV1)
SVT-AV1 is designed specifically for modern multi-core server CPUs and scales much better than older encoders.
- Use the
-tilesand-tile-columnsoptions to allow multi-threaded tile encoding. - Control SVT-AV1 threading using the
-svtav1-paramsoption:
ffmpeg -i input.mp4 -c:v libsvtav1 -svtav1-params lp=16 output.mp4The lp=16 parameter limits logical processor
utilization to 16 threads for that specific instance.