Процент загрузки CPU в Windows врёт при частичной нагрузке
TL;DR: Учёт загрузки по времени в Windows (диспетчер задач, GetThreadTimes, PDH % Processor Time) на частично нагруженном 8-ядерном 16-поточном CPU показывал до ~45% меньше, чем QueryThreadCycleTime. Чтобы понять, упираетесь ли вы в CPU, смотрите на загрузку GPU или GPU Busy в PresentMon.
В диспетчере задач игры ни разу не грузили мой 8-ядерный CPU больше чем на 30%, даже когда явно упирались в процессор. Я написал небольшой генератор нагрузки и сравнил то, что показывает Windows, с тем, что процессор делал на самом деле.
Тест
Запускаем N потоков, которые крутятся без единой блокировки. Для каждого потока раз в секунду сравниваем два числа:
- время, которое ему засчитала Windows (
GetThreadTimes, тот же источник, что использует диспетчер задач), и - реально отработанные такты (
QueryThreadCycleTime), делённые на частоту.
Что получилось
- 1 занятый поток: учёт по времени засчитал около 88% реального времени работы.
- 8–12 потоков: падает примерно до 55%.
- Заняты все 16 аппаратных потоков: оба способа показывают 100%.
- Счётчик тактов всё время показывал 98–99%, и реальная производительность с ним согласна: 8 потоков сделали в 8,2 раза больше работы, чем один.
При 8 полностью загруженных потоках общесистемный «временной» показатель был 28,5%, хотя реально была занята примерно половина машины. Другой счётчик, «% Processor Utility», ошибается в обратную сторону и показывает 80–85%, потому что масштабирует результат по частоте буста относительно базовой. Причём в простое он показывал около 13%.
Что я смотрю вместо этого
На вопрос «упираюсь ли я в CPU?» процент загрузки CPU почти не отвечает. Загрузка GPU ниже потолка или GPU Busy из PresentMon в сравнении со временем кадра отвечают на него напрямую. Заодно проверил один сторонний диспетчер задач: он читает те же временные счётчики, так что и занижает так же.