Windows CPU % under-counts partial load
TL;DR: Windows time-based CPU % (Task Manager, GetThreadTimes, PDH % Processor Time) under-reported a partially loaded 8-core/16-thread desktop CPU by up to about 45% compared with QueryThreadCycleTime. To judge a CPU bottleneck, use GPU utilization or PresentMon GPU Busy instead.
Games never seemed to push my 8-core CPU past 30% in Task Manager, even when they were clearly CPU-bound. So I wrote a small load generator and compared what Windows reports with what the CPU actually did.
The test
Start N threads that spin without ever blocking. For each thread, compare two numbers every second:
- the time Windows credits it with (
GetThreadTimes, the same source as Task Manager's "Processor Time"), and - the cycles it actually ran (
QueryThreadCycleTime), divided by the clock.
What came out
- 1 busy thread: time accounting credited about 88% of the real run time.
- 8 to 12 busy threads: it dropped to about 55%.
- All 16 hardware threads busy: both said 100%.
- The cycle counter said 98 to 99% the whole time, and throughput agreed: 8 threads did 8.2× the work of 1.
With 8 threads pegged, the system-wide "time" number showed 28.5% while about half the machine was really busy. The other counter, "% Processor Utility", went the other way and read 80 to 85%, because it scales by boost clock against the base frequency. It also showed about 13% at idle.
What I use instead
For "am I CPU-bound?" the CPU % is close to useless. GPU usage that sits below its cap, or PresentMon's GPU Busy compared to frame time, answers the question directly. I checked one third-party task manager too: it reads the same time-based counters, so it inherits the same under-count.