CPU Scaling
Modern CPUs generally all support something called CPU Scaling, where the frequency of the CPU changes to accommodate varying degrees of load on the system. After all, we’re not always doing the most demanding activities. Why should the CPU cores be running at max throttle all of the time?
Scaling is obviously a great invention that should generally be left turned on for the majority of users, but unfortunately it gets in the way of consistent measurements when we’re trying to profile code performance. Since performance measurment often counts CPU cycles, we want the CPU frequency to remain static during measurement, and between iterations of measurement.
\ Google Benchmark will warn you if scaling is enabled while profiling
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
Manually disabling CPU scaling
Disabling and re-enabling CPU scaling is done differently on each platform. This article deals with Linux systems, so we’ll focus on how to do it on Linux.
Linux
There are a few commands we can use to report and interact with this feature on Linux, but in general we’ll use the cpupower
command (manpages).
Frequency scaling governors
In general, we want to interact with the frequency scaling **governor*n * when it comes to modifying scaling.
TLDR: Generally, “disable” by setting to
performance
, and then re-enable withschedutil
.
There are a number of setting codes, described in the Arch Wiki page.
Governor | Description | Recommendation |
---|---|---|
schedutil |
Scheduler sets freq on-demand | Recommended modern default |
performance |
Lock to max frequency | Max performance/power use |
powersave |
Lock to min frequency | Not many use cases |
userspace |
Application-dependent | Highly customizable |
ondemand |
Scales CPU up quickly on load, down when idle | Replaced by schedutil |
conservative |
Auto power saving | Deprecated in modern systems |
Toggling CPU frequency settings on Linux
In my system’s case, in most circumstances, toggling between the two is simple. Check your system’s supported options and decide for yourself which ones to use.
\ DISABLE frequency scaling
# pin to max frequency for all cores
sudo cpupower frequency-set -g performance
\ ENABLE frequency scaling
# set to schedutil (modern default) for all cores
sudo cpupower frequency-set -g schedutil
\ General interaction commands
# list CPU model's supported frequency governors
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# query current frequency governor settings, list by core
cpupower frequency-info -o proc
# set all cores to a specified governor setting
cpupower frequency-set -g <setting>
Windows and MacOS
We won’t delve into the details since this is a Linux article, but here is what I could find regarding CPU scaling on these platforms. Drop a comment if you have more information to share!
Windows
There’s probably a way to write a Powershell or other script for this, but the usual way of disabling/enabling scaling is to go into the control panel to the Power Management section and set it there.
MacOS
This seems to be tricky on MacOS. The OS doesn’t natively give access to the APIs needed, so you might have to find a 3rd party application for doing this.
Other ways to influence benchmark performance
Scaling is not the only way to improve determinism and consistency when doing code profiling on a system. On a Linux system, we can occasionally benefit from core-pinning and trying to manually influence the process priority when running the actual benchmark on the compiled binary.
taskset
- pinning to a single CPU core
Use taskset
(manpages) on Linux to pin a process to a specific single CPU core, so that it doesn’t get split along other cores. This might not be useful for a multithreaded application.
nice
- set priority and run
Specify the priority (“niceness”) of the process manually specifying a number for priority with nice
(manpages)
-20
-> max priority19
-> min priority