Введение в настройку CPU для разработчиков Windows-приложений: приоритет, привязка к ядрам и P-ядра/E-ядра

· · Windows, Windows-приложения, CPU, Производительность, Приоритет, Привязка к ядрам, P-ядра, E-ядра, Энергосбережение, EcoQoS

Производительность Windows-приложения определяется не только кодом. Один и тот же .exe может ощущаться по-разному в зависимости от того, на каком ПК он работает.

На одном ПК периодическая обработка стабильна. На другом иногда возникают задержки. От сети всё в порядке, но на батарее почему-то медленно. Вы подняли приоритет в диспетчере задач, а скорость выросла не так сильно, как ожидалось. Вы вроде бы направили обработку на P-ядра, а время обработки всё равно колеблется. И наоборот: попытка повысить производительность приводит к тому, что вентилятор крутится не переставая, а другие приложения начинают тормозить.

При разработке Windows-приложений вы сталкиваетесь именно с такими явлениями, которые трудно объяснить одним лишь кодом.

В такой момент стоит посмотреть на следующие четыре аспекта.

Что смотреть Кратко говоря
Приоритет Какой поток выполняется первым
Привязка к ядрам (affinity) На каких CPU разрешено выполнение
P-ядра / E-ядра Склоняется ли это ядро к производительности или к энергоэффективности
Настройки энергосбережения Насколько интенсивно CPU разрешено работать

Кроме того, в современной Windows к этому добавляются EcoQoS и режим эффективности (Efficiency mode) в диспетчере задач.

Иными словами, среда выполнения Windows-приложения — это не просто вопрос «быстрый CPU или медленный».

Когда происходит выполнение
Где происходит выполнение
На каком типе ядра происходит выполнение
В каком состоянии производительности работает CPU
Считает ли ОС эту задачу «важной для производительности» или «допустимо выполнять энергосберегающим образом»

Именно сочетание этих факторов определяет реальную отзывчивость и время обработки.

В этой статье разбираем взаимосвязь приоритета, привязки к ядрам, P-ядер/E-ядер и настроек энергосбережения — то, что легко упустить при разработке Windows-приложений.

Код, встречающийся в этой статье, опубликован на GitHub как готовый к сборке и запуску набор примеров (библиотека и демо на C#, работающие с приоритетом и привязкой к ядрам, скрипты PowerShell и модульные тесты).

windows-app-cpu-priority-affinity-power - komurasoft-blog-samples (GitHub)

1. Сначала общая картина

Для начала представим общую картину в виде схемы.

Код приложенияПланировщик WindowsПриоритетнасколько охотно выполняетсяПривязка к ядрам / CPU Setsна каких CPU можно выполнятьсяQoS / EcoQoSориентация на производительность или на энергосбережениеВыбор потока для выполненияВыбор логического процессора для выполненияP-ядра / E-ядрасклонность к производительности или к энергоэффективностиРежим питания / план питания / PPMчастота, буст, Core ParkingРеальная отзывчивостьвремя обработкинагреврасход батареивлияние на другие приложения

Важно, что все эти факторы не независимы друг от друга.

Повысите приоритет — поток будет выполняться охотнее. Но если сам CPU управляется в сторону энергосбережения, скорость может вырасти не так, как ожидалось.

Ограничите CPU через привязку к ядрам — миграция потоков, возможно, уменьшится. Но если ограничение приходится на энергосберегающие ядра или конфликтует с Core Parking и управлением питанием, эффект может оказаться обратным.

Направите нагрузку на P-ядра — вычисления, возможно, ускорятся. Но если всё направить на высокопроизводительную сторону, вырастут нагрев, шум вентилятора, расход батареи и влияние на другие процессы.

Настройка производительности Windows-приложения — это не простой поиск «кнопки ускорения».

Какую обработку нужно ускорить? Какая обработка может быть медленной? Что приоритетнее — отзывчивость на действия пользователя? Или время завершения фоновой обработки? Насколько допустимы расход батареи и нагрев?

Это вопрос именно такого проектирования.

2. Приоритет — это «насколько охотно выполняется»

В Windows, когда одновременно готовы к выполнению несколько потоков, планировщик решает, «какой поток поставить на CPU следующим». Именно здесь работает приоритет.

В общих чертах приоритет определяется в два этапа.

Класс приоритета процесса
  + относительный приоритет потока
  = базовый приоритет потока

В терминах Win32 API на стороне процесса есть SetPriorityClass, на стороне потока — SetThreadPriority.

Чтобы в PowerShell посмотреть приоритет уже выполняющегося процесса, можно, например, так.

Get-Process -Id $PID | Select-Object Id, ProcessName, PriorityClass

Чтобы повысить приоритет текущего процесса PowerShell.

$p = Get-Process -Id $PID
$p.PriorityClass = "AboveNormal"

В C# это можно записать так.

using System.Diagnostics;

using var process = Process.GetCurrentProcess();
process.PriorityClass = ProcessPriorityClass.AboveNormal;

Здесь важно учитывать, что приоритет — это не «настройка, ускоряющая CPU». Приоритет влияет на то, какой поток выполнится первым при наличии конкуренции.

Это не настройка, повышающая частоту CPU. Не настройка, выбирающая P-ядро. Не настройка, ускоряющая ввод-вывод. Не настройка, устраняющая ожидание блокировок или сети.

Поэтому ситуация «поднял приоритет, но быстрее не стало» — совершенно обычное дело. Например, если причина медлительности в следующем, повышение приоритета не решает проблему по существу.

  • ожидание дисковых операций ввода-вывода;
  • ожидание сети;
  • ожидание ответа БД;
  • конкуренция за блокировки;
  • паузы сборщика мусора (GC);
  • блокировка UI-потока;
  • ожидание со стороны GPU или драйвера;
  • сканирование файлов антивирусом;
  • снижение частоты CPU в сторону энергосбережения.

Кроме того, HIGH_PRIORITY_CLASS и REALTIME_PRIORITY_CLASS — это не то, что стоит использовать без раздумий.

Поток, который долго работает с высоким приоритетом, неохотно уступает процессорное время другим потокам. Если бы это оставалось внутри собственного приложения — ещё ладно, но это может ухудшить отзывчивость всей системы.

Приоритет — это как лекарство.

Там, где он действует, он действует. Но увеличение дозы не значит, что станет лучше.

Что обдумать перед повышением приоритета

Прежде чем повышать приоритет, стоит подумать о следующем.

Вопрос Что нужно посмотреть
Действительно ли эта задача упирается в CPU Загрузка CPU, ETW, профилировщик
Не блокируется ли UI-поток Отзывчивость UI, переход на асинхронность, проектирование очереди
Короткий ли период работы с высоким приоритетом Поднять временно, вернуть обратно по завершении
Не помешает ли это другим приложениям Влияние на ввод, печать, браузер, резидентное ПО
Не заблокируют ли это права доступа или политика в среде клиента Права администратора, пользователь запуска, средства безопасности

Для Windows-приложения важно не постоянно работать с максимальным приоритетом, а отдавать приоритет нужной обработке в нужный момент и только в нужных пределах.

3. Привязка к ядрам (affinity) — это «на каких CPU разрешено выполнение»

Если приоритет — это «насколько охотно выполняется», то привязка к ядрам — это «на каких CPU разрешено выполнение».

В Windows для процесса или потока можно указать набор логических процессоров, на которых разрешено выполнение.

В Win32 API для процесса есть SetProcessAffinityMask, для потока — SetThreadAffinityMask.

Чтобы в PowerShell посмотреть привязку процесса к ядрам, можно, например, так.

Get-Process -Id $PID | Select-Object Id, ProcessName, ProcessorAffinity

В целях проверки, чтобы ограничить текущий процесс PowerShell первыми 4 логическими процессорами, можно написать так.

$p = Get-Process -Id $PID
$p.ProcessorAffinity = [IntPtr]0xF

0xF в двоичном виде — это 1111. То есть это означает, что разрешены логические процессоры 0-3.

Однако это исключительно пример для проверки.

Ограничение через привязку к ядрам может улучшить локальность кэша CPU и уменьшить колебания в периодической обработке. С другой стороны, это также запирает потоки в узком пространстве, хотя Windows изначально могла бы перенести их на свободные CPU.

Особая осторожность нужна в таких средах:

  • сочетаются P-ядра и E-ядра;
  • при SMT/Hyper-Threading соответствие между логическими процессорами и физическими ядрами неочевидно;
  • используется конфигурация NUMA;
  • задействованы Processor Group при более чем 64 логических процессорах;
  • включён Core Parking;
  • активно управление питанием со стороны OEM или BIOS;
  • приложение работает в виртуальной среде.

Привязка к ядрам — это настройка, которая довольно жёстко предписывает системе: «работай только на этих CPU».

Ограничение может быть инструментом стабилизации. Но ошибка в нём перекрывает пути отступления.

Сегодня есть ещё и CPU Sets

Классическая маска привязки к ядрам — довольно жёсткое ограничение.

В Windows, помимо этого, есть механизм CPU Sets. CPU Sets — это API, позволяющий приложению мягче сообщать свои пожелания по поводу CPU.

В описании Microsoft CPU Sets позиционируются как «мягкий» способ задания привязки, совместимый с управлением питанием ОС.

Нужно ли жёстко зафиксировать CPU? Или нужно лишь примерно сместить место выполнения, при этом сотрудничая с управлением питанием и планированием со стороны ОС?

Это различие важно.

Вместо того чтобы пытаться решить всё через старый добрый SetProcessAffinityMask, в современном Windows-приложении нужно учитывать и CPU Sets, и QoS.

4. P-ядра/E-ядра: «у CPU тоже есть характер»

В современных CPU не все ядра обязательно обладают одинаковой производительностью и одинаковым энергопотреблением. Характерный пример — P-ядра и E-ядра: грубо говоря, P-ядра склоняются к производительности, E-ядра — к энергоэффективности.

Тип В чём хороши
P-ядра Низкая задержка, высокая производительность в одном потоке, тяжёлая обработка на переднем плане
E-ядра Энергоэффективность, фоновая обработка, приём параллельной нагрузки

Однако разработчику опасно бездумно предполагать, что «CPU 0-7 — это P-ядра, а 8-15 — E-ядра». Порядок номеров CPU может меняться в зависимости от модели CPU, BIOS, версии Windows, прошивки, настроек OEM и среды виртуализации.

На стороне Windows в информации CPU Sets есть понятие EfficiencyClass. Это значение показывает характеристику эффективности данного CPU Set в системах с гетерогенными процессорами. Согласно документации Microsoft, чем выше это значение у CPU Set, тем более быстрый, но менее энергоэффективный процессор он представляет.

Если вы хотите работать с P-ядрами/E-ядрами, одного лишь взгляда на номера CPU недостаточно. Чтобы разобраться по-настоящему, нужны такие наблюдения:

  • смотреть EfficiencyClass через API CPU Sets;
  • смотреть, на каком CPU выполняется поток, через Windows Performance Recorder / Analyzer;
  • смотреть тенденции в отображении логических процессоров в диспетчере задач;
  • измерять время обработки на каждой конкретной машине;
  • сравнивать поведение при питании от сети и от батареи;
  • сравнивать при разных режимах питания.

P-ядра/E-ядра — это не просто аппаратная спецификация: реальное место выполнения определяется в сочетании с планировщиком Windows, QoS и настройками энергосбережения.

5. Настройки энергосбережения влияют на то, «насколько интенсивно работает CPU»

Этот момент на практике очень важен.

Приоритет — это «какой поток выполнится первым». Привязка к ядрам — это «на каких CPU разрешено выполнение». P-ядра/E-ядра — это «склоняется ли это ядро к производительности или к энергоэффективности».

А настройки энергосбережения касаются того,

на какой частоте и в каком энергетическом состоянии вообще работает CPU

Иными словами, может произойти следующее.

Вы подняли приоритет.
Направили нагрузку на P-ядра.
Но если настройки питания склоняются к энергосбережению,
CPU может не выйти на полную мощность.

Это весьма характерная для Windows история.

В Windows 11 режим питания можно выбрать в приложении «Параметры» в разделе «Система > Питание и батарея». Формулировки различаются в зависимости от среды и версии, но общее направление примерно такое.

Режим питания Направление
Максимальная энергоэффективность / Best power efficiency Приоритет батарее и энергосбережению
Сбалансированный / Balanced Баланс между производительностью и энергопотреблением
Максимальная производительность / Best performance Приоритет производительности

Кроме того, издавна существуют планы электропитания Power Saver, Balanced, High Performance. Balanced регулирует производительность и энергопотребление в зависимости от нагрузки, а High Performance — это настройка, которая жертвует энергопотреблением ради достижения максимальной производительности.

Хотя видимые пользователю настройки просты, за ними скрываются параметры управления питанием процессора — Processor Power Management, PPM.

6. P-state, C-state, буст и EPP

CPU не всегда работает на максимальной частоте — у него есть состояния для снижения энергопотребления.

Термин Кратко говоря
P-state Состояние производительности, изменяющее частоту и напряжение CPU
C-state Энергосберегающее состояние, отключающее часть функций CPU в режиме простоя
Буст Механизм перехода в состояние производительности выше номинального при подходящих условиях
EPP Energy Performance Preference. Предпочтение между производительностью и энергосбережением

P-state — это механизм снижения энергопотребления за счёт изменения частоты и напряжения CPU. C-state — это механизм, при котором CPU в состоянии простоя отключает часть функций и переходит в более глубокое энергосберегающее состояние.

Управление питанием в Windows использует эти механизмы, чтобы балансировать между производительностью и энергопотреблением.

Поэтому явление «высокий приоритет, но всё равно медленно» вполне возможно.

Поток выполняется с приоритетом. Но частота CPU низкая. Буст включается неохотно. EPP склоняется к энергосбережению. Core Parking ограничивает доступные ядра. При работе от батареи вся ОС склоняется к энергосбережению.

В таких случаях, глядя только на приоритет, до причины не добраться.

Особенно в периодической обработке, обработке изображений, управлении измерительными приборами, обработке видео и звука, захвате с USB-камеры, последовательной связи проблемой становится не только среднее время обработки, но и «периодические задержки».

В среднем быстро. Но раз из тысячи возникает задержка. Именно в этот раз буфер переполняется. UI зависает. Синхронизация с оборудованием сбивается.

Для таких явлений одной лишь средней загрузки CPU недостаточно.

Нужно вместе смотреть распределение времени обработки, максимальное значение, выбросы, состояние питания, исполняющий CPU и фоновую нагрузку.

7. Core Parking влияет на «количество доступных ядер»

В Windows есть механизм Core Parking. Это управление, переводящее неиспользуемые логические процессоры в состояние покоя. При низкой загрузке часть ядер переводится в состояние с низким энергопотреблением, что снижает общее потребление энергии.

Согласно документации Microsoft, CPMinCores — это настройка, задающая, какой минимальный процент логических процессоров должен оставаться un-parked, то есть в доступном состоянии, в любой момент времени. При значении 100% алгоритм Core Parking отключается.

Здесь проблемой становится сочетание с привязкой к ядрам.

Вы ограничили через affinity: «работай на этой группе CPU». Но то, как эти конкретные ядра трактуются управлением питанием, — отдельный вопрос.

В документации Microsoft для Windows Server тоже поясняется, что когда активные потоки жёстко привязаны к части CPU внутри узла NUMA, это иногда не согласуется с решениями Core Parking.

Как подход к рассуждению это полезно и для клиентских ПК.

Привязка к ядрам накладывает ограничение на планировщик. Core Parking связан с тем, какие ядра управление питанием делает доступными для использования.

Если рассматривать эти два фактора порознь, легко ошибиться в определении причины.

8. EcoQoS / Efficiency Mode — механизм сообщения «эту задачу можно выполнять энергосберегающим образом»

Раньше настройка производительности Windows-приложений в основном сводилась к повышению и понижению приоритета, изменению привязки к ядрам. Однако в современной Windows есть ещё один подход — это QoS.

QoS, Quality of Service, — это способ указать потоку, «насколько эта задача важна для производительности и насколько допустимо энергосбережение».

Согласно документации Microsoft, приоритет планирования остаётся основным показателем, определяющим, какой поток выполнится следующим, тогда как QoS может влиять на выбор ядра и управление питанием процессора.

В частности, EcoQoS — это механизм для энергосберегающей обработки задач, для которых производительность не является первостепенной.

В C++, например, можно перевести текущий поток в EcoQoS с помощью SetThreadInformation и ThreadPowerThrottling.

#include <windows.h>

void EnableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

И наоборот, чтобы вернуться к режиму, ориентированному на производительность, для того же объекта управления устанавливается StateMask равным 0.

void DisableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = 0;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

EcoQoS — это не функция, которую нужно применять ко всему подряд. Она подходит, например, для такой обработки:

  • фоновая синхронизация;
  • построение индекса с низким приоритетом;
  • неспешный сбор статистики логов;
  • обновление кэша, не связанное напрямую с действиями пользователя;
  • обслуживающие операции, которым достаточно завершиться когда-нибудь позже.

С другой стороны, к следующим задачам стоит подходить осторожно:

  • обработка, напрямую связанная с действиями в UI;
  • захват с камеры;
  • обработка звука;
  • обработка, связанная с циклом управления;
  • обработка результатов проверки в измерительном оборудовании;
  • экспорт, который ожидает пользователь;
  • обработка, требующая близкого к реальному времени отклика.

Efficiency mode в диспетчере задач тоже связан с этой же идеей. Согласно блогу Microsoft Performance Diagnostics, включение Efficiency mode снижает базовый приоритет процесса до Low и устанавливает QoS в EcoQoS.

Иными словами, Efficiency mode — это не просто «значок энергосбережения», а механизм, сочетающий понижение приоритета и EcoQoS, чтобы защитить отзывчивость приложения на переднем плане и энергоэффективность.

С точки зрения разработчика это довольно важно.

Ведь появляется возможность проектирования, при котором ОС сообщается не только «эта задача должна завершиться быстро», но и «эта задача может выполняться, не мешая пользователю».

9. Схема принятия решений

Изложенное выше довольно сложно, поэтому приведём схему принятия решений для исследования колебаний производительности и периодичности Windows-приложения.

Нет / неизвестноДаДаНетДаНетДаНетДаНетСимптомымедленно, колебания периода, зависание UI, шумный вентиляторСначала зафиксироватьвремя обработки / максимум / загрузка CPU / состояние питания / сеть или батарея / целевой ПКCPU — основная причина?Проверить I/O, блокировки, БД, сеть, GC, GPU, драйверысначала устранить ожидания, а не трогать приоритет или привязку к ядрамСильная конкуренция за CPU с другими процессами?Рассмотреть приоритетно ограничить область примененияизбегать постоянного использования High/RealtimeВыполнение смещено на конкретные CPU?Проверить привязку к ядрам / CPU Setsне слишком ли жёсткая фиксацияучитываются ли P/E-ядра и NUMAПодозрительны частота или состояние питания?Проверить режим питания / план питания / PPMзаподозрить P-state / EPP / буст / Core ParkingФоновая обработка мешает переднему плану?Рассмотреть EcoQoS / Efficiency Mode / понижение приоритетаперенести неспешную обработку на энергосберегающую сторонуПересмотреть алгоритм, степень параллелизма, проектирование очереди и UI-потокаМенять по одному параметру и измерять A/BСмотреть не только среднее, но имаксимум, выбросы, нагрев, влияние на другие приложения

В этой схеме важно не менять настройки с самого начала. Сначала фиксируем данные.

  • Медленно всегда или лишь иногда?
  • Питание от сети или от батареи?
  • Какой режим питания?
  • Не включён ли Efficiency mode в диспетчере задач?
  • Высокая ли загрузка CPU?
  • Растёт ли частота?
  • Какой поток использует CPU?
  • Каково максимальное время обработки?
  • Не задействовано ли другое резидентное ПО или антивирус?

После этого меняем по одному параметру.

Меняем приоритет. Меняем привязку к ядрам. Меняем режим питания. Добавляем EcoQoS. Выносим фоновую обработку отдельно. Вставляем очередь. Убираем из UI-потока.

Если менять несколько параметров одновременно, станет непонятно, что именно сработало.

10. Команды для проверки на практике

Когда поведение Windows-приложения различается в зависимости от среды, полезно уметь сначала снять состояние системы.

Посмотреть информацию о CPU

Get-CimInstance Win32_Processor |
  Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed

Посмотреть активный план электропитания

powercfg /getactivescheme

Посмотреть настройки управления питанием процессора

powercfg /q SCHEME_CURRENT SUB_PROCESSOR

Вывод довольно длинный, но можно проверить настройки, связанные с минимальным/максимальным состоянием процессора, EPP, бустом и Core Parking. Видимые пункты различаются в зависимости от среды.

Сформировать диагностический отчёт по энергоэффективности

Выполняется в терминале с правами администратора.

powercfg /energy

После измерений за определённый период выводится HTML-отчёт. Это отправная точка для изучения драйверов, устройств, разрешения таймера, энергосбережения USB, блокировщиков сна и тому подобного.

Посмотреть приоритет и привязку к ядрам целевого процесса

Get-Process -Name MyApp |
  Select-Object Id, ProcessName, PriorityClass, ProcessorAffinity

Посмотреть состояние производительности CPU

Доступные имена счётчиков зависят от среды, но эти Performance Counter будут полезным ориентиром.

Get-Counter '\Processor Information(_Total)\% Processor Performance'
Get-Counter '\Processor Information(_Total)\% Processor Utility'

Для серьёзного исследования надёжнее снять ETW через Windows Performance Recorder и Windows Performance Analyzer. Так можно проследить колебания периода, переключения контекста, исполняющий CPU, DPC/ISR, дисковый ввод-вывод и изменения частоты CPU вместе.

11. В задачах с мягким реальным временем нужно учитывать всё сразу

Windows не является операционной системой реального времени в общепринятом смысле. Однако от Windows-приложений на практике иногда требуются свойства, близкие к реальному времени.

  • получение изображений с USB-камеры с фиксированной периодичностью;
  • обмен данными с оборудованием по последовательной связи;
  • чтение данных с измерительных приборов;
  • синхронизация с ПЛК и внешними устройствами;
  • обработка звука или видео;
  • возврат результатов проверки в течение фиксированного времени;
  • выполнение тяжёлых вычислений без зависания UI.

Для такой обработки одной лишь скорости кода недостаточно.

Нужно проектирование потоков. Нужно проектирование очередей. Нужно логирование. Нужны тайм-ауты. Нужен backpressure. Нужно решить, что делать при задержке — отбросить, подождать или повторить попытку.

И сверх этого нужно учитывать приоритет, привязку к ядрам, P-ядра/E-ядра и настройки энергосбережения.

Рассмотрим, например, поток захвата с камеры.

Этот поток близок к действиям пользователя и обладает периодичностью. Возможно, его не стоит переводить в EcoQoS. Возможно, есть смысл немного повысить его приоритет. Однако при чрезмерном повышении пострадают UI и другая обработка. Фиксация привязки к ядрам может стабилизировать работу. Но если зафиксированная область смещена к E-ядрам, эффект может оказаться обратным. Если режим питания склоняется к энергосбережению, задержки могут возникать только при работе от батареи.

С другой стороны, рассмотрим задачу сжатия старых логов.

Это, возможно, обработка, которую пользователь не ждёт. В таком случае — понизить приоритет. Перевести в EcoQoS. Выполнять в режиме простоя. Выполнять только при питании от сети. Такое проектирование более доброжелательно к приложению в целом.

Вместо того чтобы делать «быстрой» всю обработку, стоит разделить задачи на те, которые должны быть быстрыми, и те, которые не должны мешать.

12. Основные принципы проектирования

Для работы с этими вопросами в Windows-приложении есть шесть основных принципов.

1. Не фиксировать с самого начала

Лучше не фиксировать жёстко приоритет или привязку к ядрам с самого начала.

Планировщик Windows во многих случаях справляется довольно хорошо. Наложение лишних ограничений со стороны приложения может, наоборот, ухудшить ситуацию.

Сначала пишем как обычно. Измеряем. При возникновении проблемы формулируем гипотезу. Вносим небольшое изменение. Снова измеряем.

Именно в таком порядке.

2. Используйте приоритет временно

Высокий приоритет нужен только на нужном отрезке времени.

Вместо того чтобы держать высокий приоритет постоянно, безопаснее

поднять его непосредственно перед важной операцией
и вернуть обратно по завершении

3. Привязку к ядрам рассматривайте в последнюю очередь

Привязка к ядрам — сильная настройка.

Она может оказаться эффективной, когда подозреваются колебания периода или влияние миграции между CPU. Но это не та настройка, с которой стоит начинать.

Особенно если у клиентов используются ПК разных типов, жёсткая фиксация номеров CPU опасна.

4. P-ядра/E-ядра — не «фиксировать заранее», а «наблюдать»

Если вы хотите разделять использование P-ядер и E-ядер, сначала понаблюдайте на реальном оборудовании.

Вместо жёсткой фиксации по номеру CPU смотрите на CPU Sets, EfficiencyClass, ETW и реальные измерения.

5. Тестируйте с учётом настроек энергосбережения

Недостаточно того, что «на машине разработчика при питании от сети и настройках высокой производительности всё быстро».

В среде клиента вполне обычны такие состояния:

  • работа ноутбука от батареи;
  • Best power efficiency;
  • Energy saver;
  • фирменные утилиты энергосбережения от OEM;
  • настройки питания, зафиксированные корпоративной политикой;
  • тонкие ПК, теряющие производительность из-за нагрева;
  • рабочие станции с большим числом резидентного ПО.

В тестировании производительности стоит как минимум раздельно рассмотреть такие условия.

Условие Что смотреть
Сеть + Best performance Состояние, близкое к максимальной производительности
Сеть + Balanced Типичное использование в бизнесе
Батарея + Balanced Реальность работы ноутбука
Батарея + Best power efficiency Нижняя граница энергосбережения
Долгая непрерывная работа Нагрев, вентиляторы, тепловой троттлинг
Одновременная работа с другими приложениями Сосуществование с браузером, Teams, Excel, антивирусом

6. Для фоновой обработки рассмотрите EcoQoS

Для задач, не связанных напрямую с пользовательским опытом, иногда лучше отдать приоритет тому, чтобы не мешать, а не гонке за производительностью.

Приложение, которое запускает всё на максимальной производительности, может быть быстрым. Но оно мешает другой работе. Раскручивает вентиляторы ноутбука. Расходует батарею. В итоге получается неудобное в использовании приложение.

Для Windows-приложений «умение уживаться» — часть качества, наряду со скоростью.

13. Частые заблуждения

Повышение приоритета всегда ускоряет работу

Не обязательно ускоряет.

Если причина в конкуренции за CPU, это может сработать. Но если причина в ожидании ввода-вывода, ожидании блокировок, низкой частоте из-за энергосбережения или выполнении на E-ядрах, одного приоритета недостаточно.

Фиксация на P-ядрах — всегда правильный выбор

Не всегда правильный.

P-ядра производительны, но при этом растут нагрев и энергопотребление. Кроме того, возможна конкуренция с другой важной обработкой на переднем плане.

Важнее разделить задачи, которые стоит направлять на P-ядра, и задачи, для которых достаточно E-ядер.

Настройки энергосбережения — дело вкуса пользователя и приложения не касаются

Касаются.

Одно и то же приложение выполняется по-разному в зависимости от режима питания, плана питания, EPP, буста и Core Parking.

Особенно на ноутбуках поведение меняется между питанием от сети и от батареи.

Efficiency mode просто всё замедляет

Не просто замедляет.

За счёт понижения приоритета и EcoQoS это механизм, защищающий отзывчивость приложения на переднем плане и энергоэффективность. Для неспешной фоновой обработки его стоит рассматривать вполне осознанно.

Windows нестабильна просто потому, что это Windows

Это тоже поверхностный взгляд.

Windows не является ОС реального времени. Но если понимать планирование, управление питанием, QoS, измерения, логирование и проектирование потоков, можно добиться вполне достаточной для практики стабильности.

Проблема не в том, что «раз это Windows — ничего не поделать», а в том, что не рассматривается, что происходит на каком уровне.

14. Проектируйте логирование раньше реализации

Проблемы такого рода иногда проявляются только в среде клиента. Поэтому полезно заранее сделать так, чтобы приложение могло сохранять хотя бы минимальную диагностическую информацию.

Например, в диагностический лог стоит выводить такую информацию:

  • версия приложения;
  • версия Windows;
  • название CPU;
  • число логических процессоров;
  • питание от сети или от батареи;
  • среднее, максимальное значение и перцентили времени обработки;
  • число задержек в цикле обработки;
  • приоритет целевого потока;
  • приоритет процесса;
  • задана ли привязка к ядрам;
  • задан ли EcoQoS;
  • план питания на момент запуска;
  • число тайм-аутов целевой обработки.

Когда клиент говорит «иногда медленно», а никаких записей нет, приходится действовать методом догадок. Если есть логи, можно строить гипотезы.

Медленно только на батарее
Медленно только на конкретном ПК
Медленно только сразу после запуска
Медленно начиная с 30-й минуты
Медленно только при работающем другом приложении
Выбросы появляются с фиксированной периодичностью

Если такие различия видны, становится проще разграничить: дело в приоритете, в привязке к ядрам, в настройках питания, в нагреве или в каком-то другом ожидании.

15. Взгляд в духе KomuraSoft

При разработке Windows-приложений одной лишь стройной теории недостаточно.

Работать на ПК клиента. Работать на реальных устройствах на местах. Сосуществовать со старой периферией. Работать в среде с антивирусом, принтерами и корпоративными политиками. Не ломаться на настройках энергосбережения ноутбука. Там, где нужна производительность, действительно её выдавать. Там, где спешить не нужно, не мешать пользователю.

Для этого лучше не рассматривать Windows просто как «чёрный ящик операционной системы».

Windows думает о том, как исполнять ваши потоки. Думает о том, на каком CPU их выполнять. Балансирует между энергопотреблением и производительностью. Работает с гетерогенными ядрами вроде P-ядер и E-ядер. Пытается различать приложения на переднем плане и фоновую обработку.

Разработчику стоит не бороться с этим, а сообщать своё намерение там, где это нужно.

Эта задача заставляет пользователя ждать. Эта задача может быть немного медленной. Для этой задачи важна периодичность. Эта задача может тихо работать в фоне. Эта задача не должна мешать другим.

Такое проектирование воплощается через понимание приоритета, привязки к ядрам, QoS и настроек энергосбережения.

16. Итог

Производительность Windows-приложения определяется не только кодом.

Даже подняв приоритет, приложение может работать не так, как ожидалось, если привязка к ядрам сместила его на E-ядра. Даже направив нагрузку на P-ядра, CPU может не выдать максимальную производительность, если режим питания склоняется к энергосбережению. Наложив жёсткую привязку к ядрам без учёта CPU Sets и QoS, вы рискуете перекрыть пути отступления для управления питанием и планировщика Windows. Направив всё на высокопроизводительную сторону, вы увеличите нагрев, шум вентилятора, расход батареи и влияние на другие приложения.

В разработке Windows-приложений нужно проектировать не только скорость, но и отзывчивость, стабильность, энергопотребление, нагрев и сосуществование с другими процессами.

И смотреть по-прежнему нужно на эти четыре аспекта:

Приоритет
Привязка к ядрам
P-ядра / E-ядра
Настройки энергосбережения

А в современной Windows к ним добавляются ещё EcoQoS и Efficiency mode.

Поводов для беспокойства немало.

Но не обязательно оставлять это беспокойство просто тревогой.

Превратите беспокойство в измерение. Превратите беспокойство в логи. Превратите беспокойство в проектирование. Превратите беспокойство в условия тестирования.

Сделайте так — и Windows перестанет быть просто капризной средой выполнения, а станет платформой для практической работы, вполне достойной внимательного наблюдения.

Windows не является ОС реального времени. И всё же, понимая среду выполнения и проектируя с её учётом, можно создавать приложения, вполне надёжные на практике.

И именно эта сложность делает разработку Windows-приложений интересной.

Справочные ссылки

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Как настроить приоритет CPU?
Приоритет определяется на двух уровнях: класс приоритета процесса и относительный приоритет потока. В Win32 API за процесс отвечает SetPriorityClass, за поток — SetThreadPriority. В PowerShell можно задать свойство PriorityClass объекта, полученного через Get-Process, например значение "AboveNormal", а в C# — присвоить ProcessPriorityClass.AboveNormal свойству PriorityClass объекта Process.GetCurrentProcess(). Однако постоянное использование HIGH_PRIORITY_CLASS или REALTIME_PRIORITY_CLASS ухудшает отзывчивость всей системы, и этого следует избегать.
Почему приложение не становится быстрее после повышения приоритета?
Потому что приоритет — это настройка, определяющая, какой поток выполнится первым при конкуренции за ресурсы, а не настройка, ускоряющая сам CPU. Если причина медлительности — ожидание дисковых операций, сети, конкуренция за блокировки, паузы сборщика мусора, блокировка UI-потока, сканирование антивирусом или снижение частоты CPU из-за настроек энергосбережения, повышение приоритета не решает проблему по существу. В первую очередь важно зафиксировать распределение времени обработки, состояние питания и исполняющий CPU, чтобы разграничить причины.
Что происходит при установке EcoQoS через SetThreadInformation?
Если указать ThreadPowerThrottling и THREAD_POWER_THROTTLING_EXECUTION_SPEED в SetThreadInformation, операционной системе сообщается, что данный поток нужно рассматривать как EcoQoS (QoS, склоняющийся к энергосбережению). QoS может влиять на выбор ядра и управление питанием процессора, поэтому такие задачи, как фоновая синхронизация или неспешный сбор статистики логов, можно перенести на энергосберегающую сторону, не мешая приложению на переднем плане. В то же время к операциям, напрямую связанным с UI, захвату с камеры или задачам с жёстким циклом управления, стоит подходить осторожно. Чтобы вернуться к режиму, ориентированному на производительность, достаточно установить StateMask в 0.
Что такое CPMinCores в powercfg?
CPMinCores — это параметр питания Windows, связанный с Core Parking, который задаёт, какой минимальный процент логических процессоров должен оставаться un-parked (доступным для использования) в любой момент времени. При значении 100% алгоритм Core Parking отключается полностью. Текущую настройку можно проверить, выполнив от имени администратора команду powercfg /q SCHEME_CURRENT SUB_PROCESSOR, и если CPU ограничен через affinity, решения Core Parking могут с этим не согласовываться, поэтому оба параметра нужно рассматривать вместе.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог