The first place most of us turn to when we’re concerned about the performance of any Mac is Activity Monitor’s CPU tab, where we can see which processes are hogging the cores. If we see anything over about 20%, we get alarmed and start asking questions, as if % meant out of a hundred. This article tries to explain that what you’re looking at probably isn’t as bad as you think, and how it can be particularly misleading on an M1 Mac.
It’s not really %
Unfortunately Apple doesn’t appear to define what the % CPU column means, other than stating that it’s “processor capability”. As far as I can tell, the figure given is the total of ‘active residency’ for all cores, meaning the percentage of core cycles that aren’t spent in idle. So if a core were completely idle, active residency would be 0%; when no idle cycles were recorded, it would be 100%.
This is the total for all CPU cores, so if your Mac has eight cores, the scale maximum for % CPU is 800%. Furthermore, when an Intel core uses hyperthreading to boost its performance, scale maximum for % CPU doubles: with eight cores in full hyperthreading, that comes to a total of 1600%.
Put the other way round, if you see % CPU at 40% and your Mac has eight cores, that’s an average active residency of just 5% on each core.
No distinction is made between E and P cores
M1 chips contain two different types of core, those designed for Efficiency (E), and those for Performance (P). They differ in the number and capability of the units within each core, and their maximum frequency. As far as % CPU goes, though, they’re all the same. This is very important in an M1 Mac, because most system processes are confined to the E cores, and almost all apps and related user processes can run on either P or E cores, preferring the P cores when possible.
You can watch this in action by opening Activity Monitor’s CPU History window, where the active residency is shown for each core separately. When background processes concerned with Spotlight indexing are taking a high % CPU, you’ll see that this is almost entirely on the E cores, leaving the P cores free to run your apps without any significant penalty from macOS.
Core frequency isn’t taken into account
Another important feature of M1 Macs is that their cores run at a wide range of frequencies (clock speeds) according to the nature and amount of their load. Both the original M1 models and the M1 Pro/Max group their cores into clusters, which are managed by macOS. The original 8-core M1 has one cluster of four E cores, and one of four P cores. The M1 Pro/Max has three clusters: two E cores in their own cluster, and two clusters of four P cores each. The second P cluster can spend much of the time almost shut down, at a frequency of 600 MHz and in full idle, until its cores are recruited for a heavy load.
When testing, frequency changes are most apparent in M1 Pro/Max chips when running background tasks on the E cores alone. A single thread is likely to be run at a frequency of around 972 MHz, but two or more see that boosted to the E core maximum of 2064 MHz, which should run code twice as fast. As that’s not taken into account when reporting % CPU, there’s a big difference in performance which remains invisible.
% CPU can be very misleading
Using my test app AsmAttic together with Activity Monitor and a command tool which reports core residency and frequency,
powermetrics, I’ve got some examples to show how misleading % CPU can be. For each of these, I’ve calculated the rate at which the chip processes loops of floating point calculations, and compare those with the % CPU shown in Activity Monitor.
Running a single thread on just the E cores of an M1 Pro gives a loop rate of 48 Mloops per second, which is reported as 98.7% CPU. Double the number of threads on the same cores and CPU doubles to 195% but the loop rate almost quadruples to 180 Mloops/s, because the frequency switches from 972 to 2064 MHz. However, when the original M1 shows twice that CPU, at 393%, it processes only 200 Mloop/s, because its E cores remain at 972 MHz and aren’t boosted to their maximum.
Run the code on the P cores instead and everything’s different. On the original M1 at an indicated CPU of 393%, its E cores managed a loop rate of 200 Mloops/s, while its P cores, showing 400%, reach a loop rate three times greater, at 594 Mloops/s.
At the upper end, there are further mismatches. For example, loading all eight cores in the original M1 is shown as 760% CPU and delivers 960 Mloops/s. An indicated 800% (from just the eight P cores) in the M1 Pro manages 1212 Mloops/s, and running its ten cores flat out hits 1403 Mloops/s and an indicated 1015%.
Equating % CPU with loop rate, each 100% CPU covers a loop rate ranging from about 50 to 150 Mloops/s.
While % CPU can be a broadly useful measure of how active the cores are, in the M1 in particular, without knowing which are most active, and what frequency they’re running at, you can be badly misled. And whatever you do, remember that % CPU isn’t on a scale from 0-100, but its maximum has to be multiplied by the number of cores.
In case this has whetted your appetite for gaining more insight into how macOS controls the cores in M1 chips, here’s my latest working proposal.