FFmpeg threads benchmark, 8 threads is 3x not 8x

Compare FFmpeg libx264 thread counts from 1 to 8 over five runs each. Eight threads was 2.99x faster than one, and pinned threads made the timings repeatable.

Share

Eight libx264 threads encoded 2.99x faster than one thread, not 8x, and four threads already delivered 2.41x. The more useful result is about measurement: with -threads pinned, five runs of the same encode landed within a few percent of each other, while the default auto setting swung 85%. Pin the thread count before you benchmark anything. The shared clip and setup are on how we benchmark.

Terminal window
ffmpeg -i sample.mp4 -c:v libx264 -crf 23 -preset medium -threads 4 -an -t 5 out.mp4

Each variant changes only the -threads value: 1, 2, 4, 8 and 0 (auto).

Thread count
Where does adding encoder threads stop helping?
Held constant: libx264 CRF 23 preset medium, same source, audio stripped
Bar chart. Thread count. Encode time and output size for each variant, each metric scaled to its own maximum.
VariantEncodeSizeCost
1 thread3644 ms387.8 KB$0.0053
2 threads2122 ms387.9 KB$0.0036
4 threads1510 ms387.7 KB$0.0029
8 threads1220 ms387.2 KB$0.0026
auto (0)1033 ms fastest433.0 KB$0.0024
Measured 2026-08-20 on the Rendobar API. Encode time is the FFmpeg step alone, separated from download and upload. 5 of 5 runs succeeded. Sizes are exact and repeatable. Timings are the median of 5 runs, and cost is from the first run. Why.

The component above prints one time and one cost per variant. For this sweep the time is the median of five runs and the cost is from the first run. Its footer note about single samples describes the other sweeps on the site, not this one. The ranges below come from the same five runs, each of which is a separate recorded job.

Four threads is the knee

ThreadsMedianSpeedup vs 1
13,644 ms1.00x
22,122 ms1.72x
41,510 ms2.41x
81,220 ms2.99x

Each doubling bought less. One to two threads gave 1.72x, two to four another 1.4x, four to eight only 1.24x more.

That is the shape Amdahl’s law predicts. Part of an encode is serial: rate control decisions depend on earlier frames, and the bitstream is written in order. Threads speed up the rest, so the curve flattens. If you are sizing a container or a worker, four threads gets most of what eight does on this kind of encode.

A 5-second clip is the hard case for threading, since the serial setup is a large share of the work. Longer inputs should scale better, so treat 2.99x as a floor for eight threads on this preset.

Pinned threads repeat, auto does not

ThreadsRange over 5 runsSpread
13,590 to 3,657 ms1.9%
22,108 to 2,181 ms3.5%
41,490 to 1,511 ms1.4%
81,183 to 1,288 ms8.9%
auto918 to 1,699 ms85%

With a pinned count, repeated runs agree to within a few percent. With auto, the same work took anywhere from 918 to 1,699 ms.

On auto, x264 picks its thread count from the CPUs it can see, roughly 1.5 threads per core. On a shared host that is likely more threads than the container’s CPU share. The count itself was stable: all five auto runs wrote the same 443,351 bytes, and a different count writes different bytes (see the next section). So the workload did not change. How quickly those threads got scheduled did, because it depends on what else the host was running.

This is also the explanation behind a 2.13x spread I found in one unpinned command run ten times, which was enough to retract a published claim. If your FFmpeg timings wander, pin the threads before you look anywhere else.

An earlier version of this section said x264’s default “asks the machine how many cores are free and sizes itself accordingly”, so it did a different amount of work each run. The byte-identical output rules that out, and the paragraph above replaces it.

Auto was fastest, and wrote a different file

auto had the fastest median at 1,033 ms, ahead of eight explicit threads. That fits auto starting more than eight threads on this host, although I did not log the count it chose.

It also wrote a different file. Auto produced 443,351 bytes. The pinned counts all produced between 396,520 and 397,244 bytes, so the auto file was about 12% larger. Each variant’s size was identical across its own five runs, so this is a real difference, not noise.

I do not have a confident explanation. x264 threads by frames by default, and each extra frame thread starts a frame before the frames it references are finished, which restricts the search a little. If that cost is non-linear it could stay negligible up to eight threads (one and eight threads came out within 0.15% of each other) and appear at whatever larger count auto chose. It stays a guess until someone logs the auto thread count.

The practical point holds without the mechanism: pinning threads changes your output, not just your speed. If you pin threads in production and benchmark on auto, you are not measuring what you ship.

What I would set

On a dedicated machine running one encode, leave it on auto and let x264 use the box.

When encodes run concurrently, pin the count to each job’s share of the CPUs. I have not measured concurrent throughput, so this rests on the contention shown above, not on a benchmark of it.

When benchmarking, always pin it. Running this sweep through the API made that cheap: each thread count was one job with the encode step timed apart from the download and upload, and five repeats of every variant ran in parallel.

Thread count is one of the settings in FFmpeg encoding settings compared. Of all of them, it is the one I would pin first, because it is the only one that decides whether the other numbers can be trusted.

Frequently asked questions

Does -threads 0 mean one thread in FFmpeg?

No. For libx264, -threads 0 means auto, and it wrote exactly the same file as leaving the flag out. Auto lets x264 choose a thread count from the CPUs it can see.

Should I pin threads when several FFmpeg jobs share a machine?

I would, though I have not measured it. Several encodes that each start threads for every core compete for the same CPUs. Giving each job a share, such as -threads 2 for four jobs on eight cores, is the usual advice.

Sources

Tags #ffmpeg#x264#performance#benchmarks
All posts
Share
  1. How we benchmark FFmpeg Engineering blog
  2. Custom fonts in a video API fail silently Engineering blog
  3. Opus vs AAC vs MP3, requested vs delivered bitrate Engineering blog