Test 4 – Real-World Multithreaded Scheduling with FFmpeg
Synthetic CPU workers give me precise control over the number of runnable processes, but I also want to look at a real multithreaded application.
I use FFmpeg and the software x264 encoder. FFmpeg is interesting because the workload consists of many threads with synchronization between them rather than a collection of entirely independent synthetic CPU workers.
I generate the video source internally, making the test repeatable.
I capture ffmpeg.data and ffmpeg-turbostat.txt while running:
cd ~/m2-scheduler-tests
sudo -v
sudo perf sched record -a \
-o ffmpeg.data \
-- sleep 70 \
> ffmpeg-perf-record.txt 2>&1 &
FFMPEG_PERF_PID=$!
sudo turbostat --quiet \
--interval 1 \
--num_iterations 70 \
--show 'CPU,Core,Busy%,Avg_MHz,Bzy_MHz,CPU%c1,CPU%c6,CoreTmp,PkgWatt' \
> ffmpeg-turbostat.txt 2>&1 &
FFMPEG_TURBO_PID=$!
sleep 2
timeout --signal=INT --kill-after=5s 60s \
ffmpeg \
-nostdin \
-hide_banner \
-loglevel warning \
-stats_period 1 \
-stats \
-f lavfi \
-i testsrc2=size=1920x1080:rate=60 \
-c:v libx264 \
-preset medium \
-f null - \
2>&1 | tee ffmpeg-workload.txt
wait "$FFMPEG_PERF_PID"
wait "$FFMPEG_TURBO_PID"
sudo perf sched timehist \
-i ffmpeg.data \
--no-call-graph \
-M -V \
> ffmpeg-sched.txt
The FFmpeg result is good, but it is considerably more complex than the fixed-worker tests. Encoding remained stable at approximately 300 frames per second while the scheduler distributed dozens of short-running threads across every core class.
FFmpeg Results
| Measurement | Result |
|---|---|
| Last recorded frame count | 17,718 at 59 seconds |
| Reported encoding rate | 300 fps |
| Encoding speed | 5.00× real time |
| FFmpeg CPU time | 678.404 CPU-seconds |
| Average fully busy CPU equivalents | 11.21 CPUs |
| P-core time | 147.254 seconds (21.71%) |
| E-core time | 352.745 seconds (52.00%) |
| LP E-core time | 178.404 seconds (26.30%) |
| CPU migrations | 460,354 |
| Steady-state migrations | 449,881 |
| Average scheduling delay | 0.065 ms |
| 95th-percentile scheduling delay | 0.420 ms |
| 99th-percentile scheduling delay | 1.011 ms |
| Maximum scheduling delay | 3.841 ms |
The progress figures settled at approximately 300 fps. During the final 20 reported intervals, FFmpeg processed an average of 300.75 frames per second. The individual one-second results ranged from 287 to 317 frames per second.
Frequency, Utilisation and Power
| Measurement | Result |
|---|---|
| Overall CPU busy | 71.60% |
| Average busy frequency | 3,254 MHz |
| Package power | 41.48 W |
| Average temperature | 77.9°C |
| Maximum temperature | 88°C |
Breaking the activity down by core class shows that the workload used every part of the processor.
| Core class | Average utilisation per CPU | Busy frequency | CPU-time share |
|---|---|---|---|
| P-core | 62.78% | 3,637 MHz | 21.71% |
| E-core | 74.19% | 3,196 MHz | 52.00% |
| LP E-core | 75.23% | 3,050 MHz | 26.30% |
The processor did not encounter the sustained power transition seen during the 16-worker stress-ng run. Package power remained broadly stable at approximately 41.5 W, below the apparent long-term limit of around 45 W. There was no corresponding late-run frequency collapse, and encoding performance remained stable.
Scheduler Interpretation
FFmpeg created 31 threads that each accumulated more than 10 seconds of CPU time. Their average uninterrupted execution period was only approximately 0.442 ms. This is fundamentally different from the stress-ng tests, where each worker was a continuously runnable thread.
The 460,354 migrations initially look excessive, amounting to approximately 7,600 migrations per second. However, they were spread across a large pool of threads processing very short pieces of work. Around 93% of the migrations involved threads labelled vf#0:0. Several lightly executing FFmpeg threads accumulated thousands of migrations while consuming very little CPU time.
The scheduler used all three core classes even though average utilisation was equivalent to approximately 11.2 fully occupied CPUs. This does not necessarily mean that the LP E-cores were selected incorrectly. Average utilisation conceals short periods when more than 12 threads may have been runnable simultaneously. Distributing these threads across all 16 CPUs can reduce queueing delays.
The low scheduling delays support that interpretation. Some 95% were below 0.420 ms, 99% were below 1.011 ms, and the worst recorded delay was 3.841 ms. The encoder sustained approximately 300 fps despite the migration activity.
Overall, this is not evidence of scheduler failure. Linux used the entire processor to service FFmpeg’s highly parallel, short-slice workload while maintaining low scheduling latency, stable frequency and consistent encoding throughput.
However, the migration count is high enough that an affinity-restricted comparison using CPUs 0 to 11 would be needed to determine whether excluding the LP E-cores improves cache locality, performance or power efficiency.
Next page: Page 7 – Foreground Work Against 12 Low-Priority Workers
Pages in this article:
Page 1 – Introduction
Page 2 – Monitoring CPU Frequency and Activity
Page 3 – Sustained Single-Thread Scheduling
Page 4 – Bursty Single-Thread Scheduling
Page 5 – Scaling from 1 to 16 CPU Workers
Page 6 – Real-World Multithreaded Scheduling with FFmpeg
Page 7 – Foreground Work Against 12 Low-Priority Workers
Page 8 – Foreground Work with All 16 CPUs Busy
Page 9 – Conclusions
Complete list of articles in this series:
| MINISFORUM M2 Core Ultra 7 356H Mini PC | |
|---|---|
| Introduction | Introduction to the series and interrogation of the machine |
| NPU | Setting up and testing the NPU |
| Benchmarks | I run a series of benchmarks focusing on the CPU, GPU, Memory, and Disk performance |
| Power | Testing and comparing the power consumption |
| BIOS | In the world of computing, BIOS, which stands for Basic Input/Output System, plays a crucial role |
| Cores | A look at Intel’s hybrid architecture |
| Intel processors | P-Cores, E-Cores and LP E-Cores Compared Across 4 Intel Processors |
| NPU | NPU and Llama |
| Scheduling | Does Linux Schedule Panther Lake Correctly? |
| Next articles in the series will continue to focus on the machine's NPU | |

Please read our Comment Policy before commenting.