Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Оптимизация на основе профилирования (PGO) использует данные, собранные во время выполнения, чтобы помочь компилятору принимать более удачные решения по оптимизации. Используя данные профиля выполнения, собранные из репрезентативных рабочих нагрузок, PGO позволяет компилятору принимать более умные решения о встраивании, макете кода и разделения горячего и холодного кода. Эти решения невозможно принимать только из статического анализа.
SPGO принимает другой подход. Вместо инструментирования двоичного файла и его прогона через синтетические сценарии обучения SPGO использует выборки данных аппаратных счётчиков производительности, собираемые из ваших реальных релизных двоичных файлов. Современные процессоры поддерживают возможности аппаратной выборки. Эти выборки можно собирать с пренебрежимо малыми накладными расходами во время выполнения, что позволяет получать профили выполнения непосредственно из кода в промышленной среде.
Так как профили SPGO выпускают биты вместо инструментированных сборок, это обеспечивает гораздо большую гибкость в том, где и как собирать данные. Профили среды выполнения можно собирать с рабочих серверов, компьютеров разработчиков, лабораторий производительности или любого сочетания. Результатом является двоичный файл, который выполняет горячие пути более эффективно, с типичным ускорением производительности 5-15% в зависимости от качества данных профиля.
В этом руководстве вы пройдёте полный цикл SPGO: создадите пример приложения, профилируете его с помощью xperf, подготовите данные профиля и повторно соберёте приложение с этими данными. По завершении можно применить тот же процесс к собственным проектам.
Предпосылки
Перед началом работы убедитесь, что у вас есть следующее программное обеспечение и оборудование.
Software
- средства сборки MSVC для x64/x86/ARM64 версии 14.51 или более поздней версии — установите их с помощью установщика Visual Studio. В разделе "Отдельные компоненты" выполните поиск по запросу "Средства сборки MSVC".
-
Windows Performance Toolkit (xperf.exe)— профилировщик
xperfсобирает примеры данных во время выполнения программы. Скачайте пакет средств для оценки и развертывания Windows (ADK) из ADK install. При запуске установщика ADK выберите компонент Windows Performance Toolkit, чтобы получитьxperf. Вам не нужно устанавливать полный ADK. - Текстовый файл «War and Peace» — используется в качестве примера рабочей нагрузки для генерации данных профилирования. Скачайте его из Project Gutenberg: https://www.gutenberg.org/ebooks/2600. Сохраните его в виде обычного текстового файла в рабочем каталоге.
Требования к аппаратному обеспечению
В руководстве представлены три варианта профилирования. Какой путь вы используете, зависит от оборудования. Команды обнаружения выполняются в разделе "Выбор метода профилирования ", чтобы узнать, какой путь поддерживает компьютер. Теперь используйте эту таблицу, чтобы подтвердить соответствие по крайней мере одному из требований.
| Path | Требование К ЦП | Примечания |
|---|---|---|
| LBR (лучшие результаты) | Регистры Last Branch Record (LBR) — это счётчики производительности, доступные на процессорах Intel Haswell (Intel Core 4-го поколения, 2013) и новее, AMD Zen 4 (2022) и новее, а также ARM64 ARMv9.2-A (2020) и новее | Предоставляет лучшие данные по ветке. Дополнительные сведения о LBR см. в статье "Введение в записи последней ветви" |
| Режим PMC/IP (хорошие результаты) | Счетчики мониторинга производительности (PMC) поддерживаются на любом ЦП x64 с единицей мониторинга производительности (PMU) | Работает на большинстве современных ЦП, где LBR недоступна. Дополнительные сведения о PMC см. в разделе "Запись событий производительности оборудования ( PMU) и запись событий производительности оборудования (PMU) с полными примерами |
| Таймер ОС (работает везде) | Любой процессор x64 или ARM64, включая виртуальные машины Azure и другие виртуальные машины | Примеры с низкой точностью, но всегда доступны |
Большинство разработчиков на современном оборудовании для настольных компьютеров x64 поддерживают LBR. Виртуальные машины и некоторые старые аппаратные компоненты имеют PMC или таймер ОС.
Как работает SPGO
SPGO собирает данные профиля из запущенного двоичного файла и передает его компилятору при следующей сборке. Компилятор использует эти данные, чтобы принимать более обоснованные решения об инлайнинге, размещении кода и предсказании переходов. Удобство заключается в том, что инструментирование не требуется.
Рабочий процесс:
- Создайте двоичный файл с помощью флага компоновщика /spgo . На этом этапе создается пустая база данных профиля образца (
.spdфайл). - Профилируйте двоичный файл с помощью
xperf, чтобы создать файл трассировки ETL. - Преобразуйте ETL-файл в SPT-файл с помощью
SPTAggregate.exe, а затем преобразуйте SPT в spD-файл с помощьюSPDConvert.exe. - Выполните повторную сборку с флагом компоновщика /spdin, указывающим на заполненную базу данных образцов профиля (SPD). Компоновщик применяет оптимизации SPGO.
Оптимизатор использует SPD, чтобы отвечать на такие вопросы, как: по каким ветвям переход выполняется чаще всего? Какие функции вызываются в горячих циклах? Этот процесс обеспечивает более качественную компоновку кода и лучшие решения о встраивании, чем один только статический анализ.
SPGO работает с C и C++. Рабочий процесс и флаги идентичны обоим языкам.
Лучшие кандидаты для SPGO: Крупные приложения на C/C++ с большим количеством ветвлений и критическими внутренними циклами. Преимущества растут по мере увеличения размера кодовой базы и сложности ветвления. Небольшой пример в этом руководстве показывает около 7% улучшения. Более крупные рабочие базы кода часто видят больше улучшений.
Сравнение процессов сборки
В этом разделе описывается, как SPGO вписывается в конвейер сборки, если вы хотите понять механику.
Обычный процесс сборки
В стандартной релизной сборке C/C++:
-
Входы: Файлы исходного кода (
.cpp,.h) и флаги компилятора в режиме выпуска (/O2и/GLт. д.). - Процесс: Компилятор применяет стандартные оптимизации, такие как встраивание эвристики, предположения прогнозирования ветви и решения макета кода на основе статического анализа. Он не имеет данных о том, как программа фактически работает при запуске.
-
Выход: Исполняемый файл (), DLL-файлы (
.exe.dll), сведения об отладке (.pdb).
Без данных времени выполнения горячие и холодные пути обрабатываются одинаково.
Процесс сборки с поддержкой SPGO
SPGO добавляет данные профилирования в качестве новых входных данных в конвейер сборки:
-
Входные данные: исходный код, файл профиля
.spd(значения счётчиков из запуска профилирования), флаги компилятора для режима Release,/link /spgoи/spdin:<path>для указания входного файла SPD (если он не указан, по умолчанию используется файл .spd с именем двоичного файла, расположенный в папке obj). - Процесс: Компоновщик считывает SPD вместе с промежуточным кодом. Он использует данные о частоте выполнения ветвей для принятия более удачных решений о встраивании, размещении кода и порядке ветвей. Горячие функции размещаются для быстрого доступа; холодный код выносится из критического пути.
-
Выход: Оптимизированный исполняемый файл (), оптимизированные файлы DLL (
.exe.dll), сведения об отладке (.pdb) и новый.spdфайл для будущих итерации профилирования.
Ключевая идея: SPGO переносит принятие решений по оптимизации от эвристик компилятора и компоновщика к решениям, основанным на данных реального выполнения.
Ключевые флаги
| Flag | Type | Purpose |
|---|---|---|
/spgo |
Linker | Включает SPGO. Внедряет метаданные SPGO в двоичный файл и создает пустой .spd выходной файл, если /spdin не указан, в этом случае указанный .spd файл используется в качестве входных данных. |
/spdin:<path> |
Linker | Входной SPD — предоставляет данные профиля компоновщику для оптимизации |
/spd:<path> |
Linker | Выходной путь SPD — указывает, где записывается новый spD (необязательно; по умолчанию используется тот же каталог, что и двоичный файл). Используется как входной путь SPD, если /spdin не указан. |
/GL |
Компилятор | Для работы SPGO между единицами трансляции требуется оптимизация всей программы |
/O1, /O2 (свести к минимуму размер, увеличить скорость) |
Компилятор | Оптимизация для скорости; обеспечивает агрессивную оптимизацию, которую SPGO может улучшить |
Как SPGO отличается от PGO
PGO (оптимизация на основе профилирования) требует компиляции исполняемого файла с флагами инструментирования (/GENPROFILE), запуска более медленного инструментированного исполняемого файла для сбора файлов со счётчиками выполнения .pgc, а затем повторной компоновки с /USEPROFILE. Компилятор получает точные счетчики выполнения, но сначала необходимо инструментировать код. Дополнительные сведения об этом процессе см. в статье "Оптимизация с помощью профилей".
SPGO использует аппаратные счетчики производительности процессора для сбора статистических выборок с вашего неинструментированного релизного двоичного файла. Запустите существующий двоичный файл, профилируйте его с помощью xperf, преобразуйте трассировку в файл SPD и перестройте. Во время профилирования нет ни инструментированной сборки, ни замедления. Компилятор получает данные статистической выборки вместо точных значений, что менее точно, но их проще получить и для этого не требуется изменять код. Это также позволяет профилировать системные компоненты или компоненты реального времени, для которых трудно собирать данные с помощью инструментированного подхода. Вы также можете профилировать бинарные файлы финальной/релизной сборки.
В этом руководстве рассматриваются три метода профилирования: LBR, PMC и таймер ОС. Выберите метод в разделе "Выбор метода профилирования". Подробное сравнение обычного процесса сборки с процессом сборки SPGO, включая эталонную таблицу флагов, см. в разделе "Сравнение процессов сборки".
Настроить perfcore.ini
⚠️ Обязательный: без этого шага
xperfне предоставляет необходимые данные профилирования. Выполните этот шаг перед запускомxperf.
Windows Performance Toolkit (WPT) использует perfcore.ini, который, если WPT установлен в расположение по умолчанию, находится по адресу C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\perfcore.ini, для регистрации DLL-поставщиков, необходимых для SPGO.
Откройте Windows Блокнот от имени администратора. Затем откройте perfcore.ini. Найдите раздел со списком DLL и добавьте следующие записи, по одной в каждой строке:
perf_spt.dll
perf_lbr.dll
Если xperf.exe не установлен, см. Общие проблемы, чтобы установить его.
Сохраните и закройте perfcore.ini. Файлы DLL уже отправлены в том же каталоге, что xperf.exe и вам не нужно копировать их в любом месте. Вы лишь регистрируете их в perfcore.ini. Убедитесь, что xperf находится в переменной PATH.
Создание примера приложения
Пример приложения для этого руководства — это программа C++, которая считывает текст из стандартных входных данных и создает число строк, число слов, общее число символов, таблицу частоты символов и истекшее время для обработки файла в миллисекундах. Он написан на C++, но SPGO также работает с C. Рабочий процесс идентичен для проектов C.
Создайте файл с именем textCount.cpp в рабочем каталоге и добавьте следующий исходный код:
// textCount.cpp : Text Statistics Counter
// Counts words, lines, and character frequencies from standard input
// Usage: textCount < file.txt
#include <iostream>
#include <string>
#include <map>
#include <cctype>
#include <chrono>
int main()
{
auto start = std::chrono::steady_clock::now();
std::map<unsigned char, int> charFrequency;
int wordCount = 0;
int lineCount = 0;
int totalChars = 0;
std::string line;
bool inWord = false;
while (std::getline(std::cin, line))
{
lineCount++;
for (char c : line)
{
totalChars++;
unsigned char uc = static_cast<unsigned char>(c);
charFrequency[uc]++;
if (std::isspace(static_cast<unsigned char>(c)))
{
inWord = false;
}
else
{
if (!inWord)
{
wordCount++;
inWord = true;
}
}
}
inWord = false;
}
std::cout << "\n=== TEXT STATISTICS ===" << std::endl;
std::cout << "Lines: " << lineCount << std::endl;
std::cout << "Words: " << wordCount << std::endl;
std::cout << "Total Characters: " << totalChars << std::endl;
std::cout << "\n=== CHARACTER FREQUENCIES ===" << std::endl;
std::cout << "\nLetters:" << std::endl;
for (unsigned char ch = 'a'; ch <= 'z'; ch++)
{
unsigned char upperCh = static_cast<unsigned char>(std::toupper(ch));
int count = charFrequency[ch] + charFrequency[upperCh];
if (count > 0)
{
std::cout << static_cast<char>(ch) << ": " << count << std::endl;
}
}
std::cout << "\nDigits:" << std::endl;
for (unsigned char ch = '0'; ch <= '9'; ch++)
{
if (charFrequency[ch] > 0)
{
std::cout << static_cast<char>(ch) << ": " << charFrequency[ch] << std::endl;
}
}
std::cout << "\nSpecial Characters:" << std::endl;
for (const auto& pair : charFrequency)
{
unsigned char ch = pair.first;
if (!std::isalnum(ch))
{
std::string displayChar;
switch (ch)
{
case ' ': displayChar = "[space]"; break;
case '\t': displayChar = "[tab]"; break;
case '\n': displayChar = "[newline]"; break;
case '\r': displayChar = "[return]"; break;
default:
if (ch >= 32 && ch < 127)
{
displayChar = std::string(1, static_cast<char>(ch));
}
else
{
displayChar = "[byte:" + std::to_string(static_cast<int>(ch)) + "]";
}
break;
}
std::cout << displayChar << ": " << pair.second << std::endl;
}
}
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration<double, std::milli>(end - start);
std::cout << "Elapsed time: " << std::fixed;
std::cout.precision(3);
std::cout << elapsed.count() << " ms\n";
return 0;
}
Создание и запуск примера для получения базовых показателей
Перед применением SPGO соберите textCount и запустите его на большом текстовом файле, например War and Peace (её можно скачать с сайта Project Gutenberg), чтобы увидеть, как быстро он работает. На этом шаге показана производительность перед оптимизацией с помощью SPGO:
Построить:
cl /EHsc /GL /O2 textCount.cpp
Запустить:
textCount.exe < warAndPeace.txt
Выходные данные похожи на следующие:
=== TEXT STATISTICS ===
Lines: 66041
Words: 566333
Total Characters: 3227531
=== CHARACTER FREQUENCIES ===
Letters:
a: 202719
...
Elapsed time: 512.000 ms
Elapsed time Запишите значение. Вы сравните его с оптимизированным для SPGO временем в разделе "Измерение результатов".
Сборка textCount с помощью /spgo
Теперь создайте textCount с поддержкой SPGO. На этом шаге лежит основа сбора данных профилирования.
cl /EHsc /GL /O2 textCount.cpp /link /debug /spgo
Когда сборка завершится, вы увидите следующее сообщение:
SPD textCount.spd not found, compiling without profile guided optimizations
Это сообщение отображается в первой /spgo сборке. Компоновщик создает SPD-файл, но он по-прежнему пуст, поэтому оптимизации SPGO пока не применяются. После того как вы запустите двоичный файл, соберёте данные профиля и преобразуете их в формат SPD, вы не увидите этого сообщения.
Пояснения по флагу:
| Flag | Purpose |
|---|---|
/EHsc |
Включение обработки исключений C++ |
/GL |
Оптимизация всей программы — необходима для SPGO. Откладывает окончательную оптимизацию до этапа компоновки, что позволяет выполнять межмодульную подстановку, оптимизировать размещение кода и удалять мёртвый код. |
/O2 |
Оптимизация для скорости — включает агрессивное встраивание, оптимизацию циклов, удаление неиспользуемого кода и другие связанные преобразования. |
/link /debug |
Передайте /debug компоновщику, чтобы сгенерировать отладочную информацию (.pdb), которую xperf использует для сопоставления выборок профилирования с исходным кодом. |
/spgo |
Флаг компоновщика SPGO встраивает метаданные SPGO в бинарный файл и создает пустой файл textCount.spd рядом с исполняемым файлом. |
Note
/spgo — это флаг компоновщика. Передайте его компоновщику с помощью /link /spgo в команде cl.
Флаг /spgo еще не оптимизирует двоичный файл. Он подготавливает его к профилированию. Оптимизация выполняется при перестроении textCount с помощью /spdin после заполнения SPD реальными данными времени выполнения.
Note
Чтобы записать SPD в указанное место, добавьте необязательный флаг компоновщика /spd:<path>. Например: /link /debug /spgo /spd:.\profiles\textCount.spd. Если этот флаг не указан, SPD создаётся вместе с .exe.
Выбор метода профилирования
SPGO поддерживает три метода профилирования. Какой метод используется, зависит от оборудования.
Три метода профилирования
| Метод | Качество образца | Требования к оборудованию | лучше всего подходит для |
|---|---|---|---|
| LBR (запись последнего перехода) | Максимальный — записывает последовательности недавно выполненных переходов по ветвлениям, предоставляя оптимизатору подробные данные о потоке управления для каждого образца | Intel Haswell (2013) или более поздней версии; AMD Zen 4 (2022) или более поздней версии; ARM64 ARMv9.2-A (2020) или более поздней версии | Большинство современных настольных аппаратных средств |
| PMC / IP-режим (режим счетчика мониторинга производительности или указателя инструкций) | Хорошо. Записывает примеры указателя инструкций с стеками вызовов с помощью модуля мониторинга производительности ЦП (PMU), собранных с помощью трассировки событий для Windows (ETW) | Любой ЦП x64 или ARM64 с PMU | Оборудование без поддержки LBR |
| Таймер ОС | Базовые примеры на основе таймера | Любой процессор x64 или ARM64, виртуальные машины без проброса PMU | Виртуальные машины и более старое оборудование |
В режиме PMC / IP каждое аппаратное прерывание дает только одну точку данных: "ЦП был на адресе 0x1A2B3C4D при срабатывании прерывания". При использовании LBR каждое прерывание предоставляет стек последних 16–32 переходов, выполненных ЦП до срабатывания прерывания. Оптимизатор получает более точные данные о потоке управления и может принимать более удачные решения по инлайнингу и размещению кода.
Определите путь
Выполните следующие две команды, чтобы определить, какой путь профилирования поддерживает компьютер. Для этих команд не требуется командная строка, запущенная с повышенными правами.
Шаг 1. Проверка поддержки LBR. Этот тест работает на Intel/AMD/ARM64.
Выполните следующую команду в командной строке разработчика Visual Studio, запущенной от имени администратора:
xperf.exe -on PMC_PROFILE -pmcprofile TotalIssues -LastBranch PmcInterrupt -setProfInt TotalIssues 2560000
xperf -stop -d lbrtest.etl
xperf -tle -i lbrtest.etl -a dumper | findstr "LBR, TimeStamp"
- Если эта команда находит строку, содержащую
LBR, TimeStamp, компьютер поддерживает LBR. Используйте путь LBR. - В противном случае перейдите к шагу 2.
Шаг 2. Проверка поддержки PMC (без LBR)
xperf.exe -pmcsources | findstr TotalIssues
- Если эта команда создает выходные данные, компьютер поддерживает счетчики PMC, но не LBR. Используйте путь PMC.
- Если эта команда не создает выходных данных, используйте путь таймера ОС.
Дополнительные сведения о коллекции событий PMU с xperf см. в разделе "Запись событий аппаратного PMU" с помощью xperf.
Таблица принятия решений
LBR, TimeStamp Выход |
TotalIssues выходные данные |
Ваш путь |
|---|---|---|
| Не пуст | (не проверено) | LBR |
| Пусто | Не пуст | PMC |
| Пусто | Пусто | Таймер ОС |
| Процессор ARM64 | N/A | PMC (если PMU доступен) или таймер ОС |
Выбор подхода
Определите, следует ли использовать путь таймера LBR, PMC или ОС на основе результатов обнаружения. Каждый путь имеет разные xperf параметры запуска для сбора соответствующих данных профилирования. Выберите вариант, который соответствует возможностям вашего оборудования.
Путь:
- Пользователи LBR (LBR обнаружен на шаге 1): Перейдите к пути LBR.
- Пользователи PMC (InstructionRetired обнаружен на шаге 2): Перейдите к пути PMC (без LBR).
- Пользователи таймера ОС (виртуальная машина или оборудование без PMU): перейдите к пути таймера ОС.
Все пути снова сходятся на этапе Запустите рабочую нагрузку и остановите xperf.
Команды в этом разделе зависят от пути профилирования, который вы определили в разделе Выбор метода профилирования. Найдите подраздел, соответствующий вашему пути, выполните команду xperf start, а затем перейдите к разделу Запуск рабочей нагрузки и остановка xperf, чтобы запустить рабочую нагрузку и остановить xperf.
⚠️ Запуск от имени администратора:
xperfтребуется командная строка разработчика с повышенными привилегиями (администратор). Без повышения привилегийxperfвозвращает"failed to configure counters".
Путь LBR
Начните xperf с коллекции LBR:
xperf -on LOADER+PROC_THREAD+PMC_PROFILE -MinBuffers 4096 -MaxBuffers 4096 -BufferSize 4096 -pmcprofile BranchInstructionRetired -LastBranch PmcInterrupt -setProfInt BranchInstructionRetired 16384
Объяснение параметров:
| Parameter | Purpose |
|---|---|
LOADER+PROC_THREAD+PMC_PROFILE |
Поставщики ядра: события загрузчика (сопоставление модулей), события процесса или потока (контекст выполнения) и события профилирования PMC |
-MinBuffers 4096 -MaxBuffers 4096 -BufferSize 4096 |
Большие кольцевые буферы для предотвращения потери выборок во время полного прогона «Войны и мира» |
-pmcprofile BranchInstructionRetired |
Триггер события PMC: генерировать сэмпл на каждой N-й завершённой инструкции ветвления |
-LastBranch PmcInterrupt |
Включает запись оборудования LBR: на каждом прерывании PMC запишите стек записей последней ветви оборудования. |
-setProfInt BranchInstructionRetired 16384 |
Интервал выборки: генерировать прерывание каждые 16 384 завершённых инструкций ветвления |
После запуска xperf перейдите к рабочей нагрузке и остановите xperf.
Путь PMC (без LBR)
Начните xperf со сбора данных в режиме PMC/IP:
xperf -on LOADER+PROC_THREAD+PMC_PROFILE+PROFILE -MinBuffers 4096 -BufferSize 4096 -pmcprofile InstructionRetired -setProfInt InstructionRetired 16384 -stackwalk profile
Объяснение параметров:
| Parameter | Purpose |
|---|---|
LOADER+PROC_THREAD+PMC_PROFILE+PROFILE |
Добавляет PROFILE (семплирование ЦП) и PMC_PROFILE для событий PMC; без -LastBranch |
-pmcprofile InstructionRetired |
Триггер события PMC: выборка по завершённым инструкциям (режим указателя команд) |
-setProfInt InstructionRetired 16384 |
Генерировать прерывание каждые 16 384 завершённых инструкций |
-stackwalk profile |
Захватывать стек вызовов при каждом прерывании профилирования с получением данных о цепочке вызовов вместо последовательностей переходов |
По сравнению с LBR: флаг -LastBranch отсутствует; используется InstructionRetired вместо BranchInstructionRetired. Результатом являются примеры указателя инструкций с стеками вызовов, а не последовательности ветвей. Этот путь по-прежнему предоставляет оптимизатору полезные данные, но они несколько менее полные.
После запуска xperf перейдите к разделу Запуск рабочей нагрузки и остановка xperf.
Путь таймера ОС
Запуск xperf с выборкой на основе таймера ОС:
xperf -on LOADER+PROC_THREAD+PROFILE -MinBuffers 4096 -BufferSize 4096 -setProfInt Timer 1221 -stackwalk profile
Объяснение параметров:
| Parameter | Purpose |
|---|---|
LOADER+PROC_THREAD+PROFILE |
События PMC отсутствуют; профилирование ЦП выполняется только через прерывание таймера ОС |
-setProfInt Timer 1221 |
Срабатывание прерывания таймера ОС каждые 1221 таймера (приблизительно 1 кГц) |
-stackwalk profile |
Запись стека вызовов при каждом прерывании таймера |
По сравнению с LBR и PMC этот метод не использует аппаратные счетчики производительности. Таймер ОС запускается примерно с фиксированным интервалом времени независимо от активности ЦП. Примеры менее плотно коррелируются с горячим кодом, но по-прежнему предоставляют полезные данные потока управления для оптимизатора.
Запустите рабочую нагрузку и остановите xperf (все пути)
Когда xperf запущен, запустите textCount на «Войне и мире»:
textCount.exe < warAndPeace.txt
После завершения textCount остановите xperf и запишите файл трассировки. Если во время профилирования позволить выполняться другим процессам, это снизит качество выборки. Для получения наилучших результатов закройте ненужные приложения перед выполнением рабочей нагрузки.
xperf -stop -d textCount.etl
После остановки xperf (запись файла ETL может занять некоторое время) убедитесь, что в текущем каталоге был создан textCount.etl.
Преобразование ETL-файла в SPT
Этот шаг одинаков для всех трех путей профилирования.
Запустите SPTAggregate.exe, чтобы обработать необработанную трассировку ETL и создать файл профиля SPT:
SPTAggregate.exe /binary textCount.exe /etl textCount.etl textCount.spt
Объяснение параметров:
| Parameter | Purpose |
|---|---|
/binary textCount.exe |
Двоичный файл, из которого нужно извлечь примеры. ETL может содержать примеры из всех процессов, выполняемых во время профилирования |
/etl textCount.etl |
Входной файл трассировки ETL |
textCount.spt |
Выходной файл профиля SPT |
SPTAggregate выводит сводку, показывющую количество собранных примеров. Это первое подтверждение того, что профилирование сработало.
Сверьте результат из SPTAggregate с путём, который вы выбрали:
- Путь LBR: Найдите ненулевое значение счётчика «Используемые выборки LBR».
- Путь PMC: Найдите ненулевое количество выборок PMC или стека.
- Путь таймера ОС: Найдите ненулевое число использованных выборок стека.
Если все счетчики равны нулю, см. раздел "Устранение неполадок " перед продолжением.
Преобразование SPT-файла в SPD
Путь:
- Пользователи LBR (использование
/mode:LBR): режим LBR- Пользователи PMC (использование
/mode:IP): режим IP (PMC и таймер ОС)- Пользователи таймера ОС (используйте
/mode:IP): режим IP (PMC и таймер ОС)И пути таймеров PMC, и ОС используют
/mode:IP, поскольку оба формируют выборки указателя команд.
Следующий шаг разветвляется в зависимости от пути профилирования, а именно от флага /mode, передаваемого в SPDConvert.exe.
Режим LBR
SPDConvert.exe /mode:LBR textCount.spd textCount.spt
/mode:LBR указывает SPDConvert интерпретировать SPT как содержащий данные о последовательности ветвлений LBR.
Режим IP (PMC и таймер ОС)
Оба таймера PMC и OS создают примеры указателя инструкций, поэтому оба используют одну и ту же команду преобразования:
SPDConvert.exe /mode:IP textCount.spd textCount.spt
/mode:IP указывает SPDConvert интерпретировать SPT как содержащие примеры указателя инструкций.
Предупреждение
Использование неправильного режима для типа данных может создать пустой или неправильно сформированный SPD. Если вы использовали LBR для профилирования, используйте /mode:LBR.
Если профилирование выполнялось с помощью PMC или таймера ОС, используйте /mode:IP. Сводные выходные данные SPTAggregate из Convert the ETL file to SPT показывают, какие типы выборок были собраны, и подтверждают, какой режим следует использовать.
После выполнения SPDConvert убедитесь, что textCount.spd был создан (или обновлён) в текущем каталоге.
Интерпретация выходных данных SPDConvert
Команда SPDConvert textCount.spd textCount.spt выводит сводку покрытия блоков до и после, например:
Block coverage (before) : 33.90% ( 4507/ 13294)
Block coverage (after) : 45.64% ( 6067/ 13294)
В этой сводке показан процент блоков кода двоичного файла, для которых имеются соответствующие данные профилирования. Более высокий процент лучше. Покрытие выше 70% отлично, а покрытие ниже 40% может ограничить эффективность оптимизации. Если покрытие недостаточно, выполняйте нагрузку для профилирования дольше или объедините несколько файлов SPT из отдельных запусков с разными рабочими нагрузками. Например, можно запустить textCount на нескольких текстовых файлах, чтобы проверить разные пути выполнения кода.
Вы можете увидеть предупреждение от SPDConvert примерно такого вида:
Compiler may be conservative on some hot functions due to sparse sample coverage.
SPGO is estimated to optimize better if sample density is increased to 5.4x of current level.
Sample density can be increased by sampling for longer period, or increasing sample rate.
Это предупреждение означает, что во время профилирования было собрано недостаточно образцов, чтобы оптимизатор мог уверенно оптимизировать все горячие функции. SPD по-прежнему подходит для использования, но вы можете улучшить результаты, выполнив следующие действия.
- Выполнение рабочей нагрузки дольше (например, 5 или более минут вместо 1 минуты) или использование разных рабочих нагрузок.
- Уменьшение
-setProfIntзначения в командеxperfдля увеличения частоты выборки. Компромисс заключается в том, что это изменение создает больший ETL-файл, который занимает больше времени для обработки. - Объединение нескольких SPT-файлов из отдельных запусков профилирования, передавая их все в
SPDConvert.
Файл SPT — это двоичный формат. Чтобы проверить его содержимое, можно запустить SPTDump.exe textCount.spt. Аналогично, PTDump.exe textCount.spt показывает скомпилированные данные профилирования после выполнения SPDConvert. Оба средства полезны для проверки ненулевого образца перед продолжением работы.
Перестроить textCount с /spdin
Перестройте textCount с помощью заполненного SPD-файла. Компоновщик считывает данные профиля и применяет оптимизации SPGO.
Этот шаг одинаков для всех трех путей профилирования.
cl /EHsc /GL /O2 textCount.cpp /link /debug /spgo /spdin:textCount.spd
Новый флаг (по сравнению с Build textCount with /spgo):
| Flag | Purpose |
|---|---|
/spdin:textCount.spd |
Укажите данные профиля SPD компоновщику для оптимизации |
Команда по-прежнему включает /spgo. Он создает новый файл SPD вместе с оптимизированным двоичным файлом, который можно использовать в качестве отправной точки для последующих итераций профилирования.
Предупреждение
Файл SPD связан с конкретным двоичным файлом, который он профилирует. Если вы выполняете повторную сборку textCount без /spdin или повторную сборку из изменённого исходного кода, необходимо создать новый SPD-файл. Существующий файл не соответствует GUID нового двоичного файла, и компоновщик не сможет его использовать.
После повторной сборки с /spdin компоновщик выводит статистику о том, какая часть вашего кода была оптимизирована с помощью данных профилирования. Рассмотрим пример.
221 of 221 (100.00%) profiled functions will be compiled for speed
201 of 1383 inline instances were from dead/cold paths
474 of 474 profiled functions (100.0%) were optimized using profile data
202738780 of 202738780 instructions (100.0%) were optimized using profile data
Высокий процент означает, что SPD хорошо охватывает ваш бинарный файл. Если процент низкий (например, ниже 90%), это означает, что либо нагрузка при профилировании не охватила достаточную часть двоичного файла, либо двоичный файл значительно изменился с момента сбора профиля. В обоих случаях повторно выполните профилирование по текущему бинарному файлу.
Что spGO делает с данными профиля
SPGO использует собранные выборочные данные для заполнения значений счётчиков в каждом блоке и на каждом ребре графа потока управления программы. Эти показатели используются для таких оптимизаций, как:
- Встраивание, управляемое профилированием: агрессивно встраивать горячие места вызова, избегая раздувания кода из-за встраивания холодных путей.
- Разделение горячего и холодного кода. Перемещение редко выполняемого кода в отдельные разделы двоичного файла, улучшение использования кэша инструкций и поведение разбиения на страницы.
- Макет функции: размещение функций, которые часто вызывают друг друга в двоичном файле, сокращая ошибки страниц и повышая локальность. Оптимизированные функции сгруппированы в COFF-группы с высокой степенью сходства в бинарном файле.
- Решения по размеру и скорости: компилируйте часто вызываемые функции с приоритетом скорости, а редко вызываемые — с приоритетом уменьшения размера. Подпрограммы, для которых в профиле не зафиксировано вызовов, могут оптимизироваться под размер кода, а не под скорость, что ограничивает такие оптимизации, как встраивание и разворачивание циклов, в этих редко выполняемых участках кода.
- Спекулятивная девиртуализация: когда выборка показывает, что косвенный вызов последовательно нацелен на ту же функцию, SPGO может спекулировать на этом целевом объекте и встраивает его с резервным вариантом для редкого случая.
Измерение результатов
Запустите textCount снова и сравните истекшее время.
textCount.exe < warAndPeace.txt
Соберите несколько запусков для каждой конфигурации и используйте медиану. Один запуск не даёт надёжного результата, поскольку планировщик ОС и системный шум могут исказить результаты отдельных измерений.
| Сборка | Типичное затраченное время |
|---|---|
Базовые показатели (cl /EHsc /O2) |
(ваше измерение) |
/spgo сборка (пока нет данных профиля) |
(должно быть близко к базовому значению) |
Оптимизировано для SPGO (/spdin) |
(должно показать улучшение) |
В одном тесте SPGO с использованием метода LBR обеспечило сокращение времени выполнения примерно на 7%. В ваших проектах результаты могут отличаться, поскольку прирост от SPGO зависит от того, насколько точно нагрузка, используемая для профилирования, отражает типичное выполнение. Более крупные, заполненные ветви базы кода, как правило, видят больше улучшений в диапазоне 5–10%. Метод профилирования влияет на качество оптимизации. LBR обычно дает лучшие результаты, чем PMC, что дает лучшие результаты, чем таймер ОС. Если вы используете ветку с таймером ОС, ожидайте меньшего прироста.
Путь LBR, приведенный в этом руководстве, был применен к проекту SQLite , который является рабочей библиотекой базы данных. Бинарная сборка SQLite, оптимизированная с помощью SPGO, показала улучшение примерно на 7%.
Применение SPGO к собственному проекту
Используйте этот контрольный список, чтобы применить SPGO к собственному приложению C или C++ .
Добавьте
/link /spgoк вашей текущей команде сборки релизной версии. Измените скрипт сборки или файл проекта:cl /EHsc /GL /O2 myapp.cpp /link /spgoВыберите представительную рабочую нагрузку. Выберите реальный сценарий использования, который задействует наиболее часто используемые пути выполнения вашего приложения. Используйте данные, приближенные к производственным. Не используйте в качестве основной нагрузки для профилирования следующее: тесты на покрытие кода (они не нагружают узкие места производительности), редкие сценарии обработки ошибок, этапы запуска и завершения работы, а также устаревшие ветки кода. Эта рабочая нагрузка управляет профилем, который передает оптимизатор.
Запустите xperf, используя найденный путь. Используйте путь, указанный в разделе "Выбор метода профилирования " (LBR, PMC или таймер ОС). Запустите
xperf, выполните рабочую нагрузку один раз, остановитеxperfи соберите ETL-файл.Для пути таймера PMC или ОС запустите SPTAggregate и SPDConvert с правильным
/modeфлагом. Преобразуйте ETL в SPT, а затем в SPD. Используйте/mode:LBRдля данных LBR; используйте/mode:IPдля данных таймера PMC или ОС.Пересобрать с помощью
/spdin:<your-spd-path>. Скомпилируйте приложение с заполненным SPD:cl /EHsc /GL /O2 yourApp.cpp /link /spgo /spdin:yourApp.spdИзмерьте до и после. Запустите рабочую нагрузку с неоптимизованными и оптимизированными для SPGO двоичными файлами. Соберите медиану нескольких запусков для каждой конфигурации. Один запуск не является надежным для тестирования.
Сохраните файл
.spdв системе контроля версий. Добавьте файл.spdв систему контроля версий вместе с исходным кодом.Включите SPGO в релизных сборках для разработчиков. Настройте сборки Release вашей команды так, чтобы они использовали те же бинарные файлы, оптимизированные с помощью SPGO, что и в рабочей среде. Это помогает выявлять регрессии производительности на раннем этапе.
Отключите SPGO в сборках отладки.
Отслеживайте статистику заполненности профиля линкера. После каждой сборки с
/spgoотмечайте процент профилированных функций, оптимизированных с использованием данных профиля. Если это значительно снижается (ниже 90%), перепрофилировать текущий двоичный файл. Изменения в коде накапливаются, и SPD может стать неактуальным.
Альтернатива использованию xperf
Еще один способ собирать данные профилирования — использовать профилировщик с выборкой, например Средство записи производительности Windows (WPR). WPR устанавливается по умолчанию на Windows 10 и более поздних версиях. Он собирает такие же данные, как и xperf. Вы можете настроить WPR на сбор выборок ЦП со стеками вызовов, а затем экспортировать данные в файл ETL, который можно обработать с помощью SPTAggregate и SPDConvert, как и xperf ETL. Ниже приведен пример использования WPR для сбора данных профиля:
wpr -start CPU.light -filemode
textCount.exe < warAndPeace.txt
wpr -stop spgo_data.etl
Дополнительные сведения об использовании WPR см. в разделе Using Windows Performance Recorder.
Распределение SPD
Можно:
- Добавьте файл
.spdнепосредственно в систему контроля версий наряду с исходным кодом. - Поделитесь файлом
.spdс коллегами, чтобы они могли выполнять сборку с оптимизациями SPGO без повторного профилирования. - Упакуйте файл
.spdвместе с вашими двоичными файлами как артефакт с указанием версии (например, пакет NuGet) и запишите, какая версия соответствует какому двоичному файлу. - Повторно создайте
.spdфайл в любое время, повторив рабочий процесс профилирования.
SPD привязан к точному бинарному файлу, из которого он был собран. После значительных изменений кода повторно создайте новый SPD. Во время сборки /spdin компилятор также создает новый .spd файл. Сохраните этот новый SPD в качестве артефакта сборки — это отправная точка для следующей итерации профилирования.
Повторное использование информации SPD в разных сборках
Концепция «переноса» в SPGO позволяет добавлять данные профилирования в существующий файл SPD без необходимости заново профилировать все сценарии с нуля и без потери имеющихся данных профиля. Вы также можете настроить, какой вес придавать более старым данным профиля. Эта гибкость полезна, если с течением времени могут возникнуть изменения поведения, и вы не хотите полностью потерять сведения о профилировании из более ранних сценариев. Например, по мере развития приложения, которое вызывает DLL, могут вызываться разные API. Вы по-прежнему хотите сохранить оптимизации, основанные на прежнем поведении, но также хотите добавить возможности оптимизации для случаев, когда теперь оно иногда ведет себя иначе. Профиль можно развивать с течением времени, смешивая старые и новые данные.
При запуске SPDConvert с новым SPT-файлом передайте имя существующего SPD-файла. Затем используйте параметр /retire:N, чтобы управлять тем, насколько агрессивно SPDConvert снижает значимость старых данных профиля при добавлении новых SPT-файлов:
- Параметр по умолчанию (
/retire:8) придаёт больший вес более новым данным. - Используйте
/retire:0, чтобы присвоить одинаковый вес всем запускам. - Используйте
/retire:16, чтобы учитывались только самые новые данные.
Troubleshooting
Найдите проблему:
- Проблемы путей LBR:Проблемы путей LBR
- Проблемы пути PMC:проблемы пути PMC
- Проблемы таймера ОС:проблемы пути таймера ОС
- Проблемы, влияющие на все пути:Общие проблемы
Проблемы с путем LBR
| Проблема | Вероятно, причина | Исправить |
|---|---|---|
Нет образцов LBR в выходных данных SPTAggregate |
ЦП не поддерживает LBR или виртуальная машина не предоставляет LBR | Выполните команду обнаружения в разделе Определите свой путь. Если вы работаете в виртуальной машине Hyper-V, запустите Set-VMProcessor MyVMName -Perfmon @("pmu", "lbr") на хосте. Если LBR недоступен, переключитесь на ветку PMC или таймера ОС. |
Процессор поддерживает LBR, но SPTAggregate показывает 0 примеров LBR |
perfcore.ini Неполная регистрация библиотеки DLL |
Выполните настройку perfcore.ini в разделе "Настройка perfcore.ini". Убедитесь, что perf_lbr.dll зарегистрировано. |
SPDConvert не работает или создает пустой SPD |
Неправильный /mode флаг или SPT содержит только примеры в режиме IP |
Убедитесь, что в выводе SPTAggregate отображаются образцы LBR. Если выходные данные отображают только примеры в режиме IP, переключитесь на /mode:IP. |
Проблемы с путем PMC
| Проблема | Вероятно, причина | Исправить |
|---|---|---|
Ноль образцов PMC в выходных данных SPTAggregate |
perfcore.ini Некорректная регистрация DLL |
Выполните настройку perfcore.ini в разделе "Настройка perfcore.ini". Убедитесь, что perf_spt.dll зарегистрировано. Без этой библиотеки DLL xperf не выдаёт ни одного образца PMC и не выводит сообщения об ошибке.Запустите xperf.exe -pmcsources, чтобы увидеть список источников счётчиков производительности, доступных на вашем процессоре. Если вы не видите такие записи, как SPT_OP_RETIRE_INSTR, SPT_OP_RETIRE_BR_INSTR или SPT_OP_ETW_INSTR, то регистрация DLL в perfcore.ini может быть неполной, или ваш процессор не поддерживает PMC. Если вам не удаётся устранить проблему с регистрацией DLL, попробуйте вместо этого использовать путь таймера операционной системы. |
findstr InstructionRetired возвращает выходные данные, но xperf не создает примеры |
Маскирование счетчиков PMC в виртуальной машине | Проверьте, запущена ли виртуальная машина. Включите PMU в Hyper-V с помощью Set-VMProcessor или переключитесь на путь таймера ОС. |
SPDConvert сбой при использовании пути PMC |
Использование /mode:LBR на SPT с поддержкой только IP |
Переключитесь на /mode:IP. |
Проблемы тракта таймера ОС
| Проблема | Вероятно, причина | Исправить |
|---|---|---|
| Меньше улучшения, чем ожидалось | Это ожидаемо: системный таймер имеет меньшую точность. | Это нормально. Оптимизатор располагает меньшим объёмом информации о переходах из выборок таймера, чем из LBR или PMC. Прирост производительности меньше. Если оборудование поддерживает его, рассмотрите возможность обновления до PMC или LBR. |
| Примеры нулевого таймера |
xperf не был запущен в запросе с повышенными привилегиями или PROFILE отсутствует поставщик |
Подтвердите выполнение от имени администратора. Убедитесь, что -stackwalk profile был передан команде xperf. |
Общие проблемы (все пути)
| Проблема | Вероятно, причина | Исправить |
|---|---|---|
"failed to configure counters" Ошибка |
xperf не работает от имени администратора |
Перезапустите командную строку от имени администратора (щелкните правой кнопкой мыши запуск > от имени администратора). Xperf требует повышенных привилегий для настройки счетчиков производительности оборудования. |
xperf не найдено |
xperf.exe не найден в PATH |
Убедитесь, что установлен Windows ADK. Проверьте C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\. Добавьте этот каталог в path или запустите xperf напрямую. |
textCount.etl не создано |
xperf завершился сбоем без сообщения об ошибке | Подтвердите выполнение от имени администратора. Повторно выполните команду xperf start и проверьте, выводятся ли сообщения об ошибках. |
SPTAggregate завершается с ошибкой «двоичный файл не найден» |
textCount.exe не в текущем каталоге или неправильном пути |
Убедитесь, что вы находитесь в том же каталоге, что textCount.exeи укажите полный путь к параметру /binary . |
| Файл SPD не создан |
SPDConvert не удалось |
Убедитесь, что размер textCount.spt не равен нулю. Запустите SPTDump.exe textCount.spt, чтобы проверить его содержимое. |
/spdin Сборка не дает улучшений |
Несоответствие GUID и возраста между SPD и двоичным файлом | SPD был построен из другого textCount.exe. Профилируйте текущую сборку ещё раз, чтобы создать новый SPD. |
Ошибка версии MSVC для /spgo |
Набор инструментов MSVC раньше версии 14.51 | Откройте установщик Visual Studio >Отдельные компоненты> и установите MSVC v14.51 или более позднюю версию. Откройте командную строку разработчика. |
Дальнейшие действия
После работы с этим руководством изучите эти возможности, чтобы получить дополнительные сведения из SPGO:
-
Слияние профилей: Запустите несколько нагрузок, накопите файлы SPT из каждого запуска и передайте их в
SPDConvert. Смешанный SPD отражает полный спектр реальных шаблонов использования и обеспечивает лучшую оптимизацию, чем профиль одного сценария. Используйте параметр/retire:N, чтобы настроить, насколько сильноSPDConvertснижает значимость старых данных профиля при добавлении новых файлов SPT. Параметр по умолчанию (/retire:8) придаёт больший вес более новым данным. Используйте/retire:0, чтобы придать всем запускам одинаковый вес; используйте/retire:16, чтобы учитывались только самые новые данные. - Наилучшие результаты получаются при сочетании профилей из нескольких источников, таких как эталонные тесты, охватывающие ключевые сценарии, а также данные из реальных условий (если они доступны). Передавайте SPT-файлы из всех источников в
SPDConvert. Повторите файл SPT в списке аргументов, чтобы придать ему больший вес (например,SPDConvert myapp.spd critical.spt critical.spt common.sptпридаётcritical.sptвдвое больший вес, чемcommon.spt). -
Итеративная оптимизация: Каждое перестроение с
/spdinсоздаёт новый SPD. Вы можете повторить цикл запуска, профилирования и пересборки. Последующие итерации могут демонстрировать убывающую отдачу, но второй проход иногда позволяет выявить закономерности, которые были упущены при первом. - Изменения кода: После существенных изменений исходного кода повторно соберите данные профилирования. Существующий SPD привязан к двоичному файлу, на который он был профилирован. Он не будет совпадать с существенно изменённым двоичным файлом.
-
Свежесть профиля: После каждой
/spdinсборки компоновщик сообщает, какой процент профилированных функций был оптимизирован на основе данных профиля. Если этот процент значительно снижается, это сигнал, что код отклонился от профиля. Перепрофилировать текущий двоичный файл.