title | seoTitle | seoDescription | datePublished | cuid | slug | cover | tags |
---|---|---|---|---|---|---|---|
Профилирование C++ кода в Visual Studio без инструментирования |
Профилирование C++ кода в Visual Studio без инструментирования |
Разбираемся, как решать проблемы производительности программы с помощью профилировщика Visual Studio без инструментирования |
Wed Dec 13 2023 14:08:43 GMT+0000 (Coordinated Universal Time) |
clq3ukcu2000f08jvfjtr3we8 |
profilirovanie-c-koda-v-visual-studio-bez-instrumentirovaniya |
optimization, cpp, visual-studio, profiling, optimizaciya-koda, profilirovanie, profilirovanie-c |
Рассмотрим код с проблемой производительности - он выполняется слишком долго. Проблема может показаться очевидной. Да что там показаться, так оно и есть. Но представим, что мы не знаем, где проблема.
#include <iostream>
#include <fstream>
using namespace std;
void WriteFile1(int n)
{
for (int i = 0; i < n; ++i)
{
fstream file("file.txt");
if (file)
file << i << " ";
}
}
void WriteFile2(int n)
{
for (int i = 0; i < n; ++i)
{
fstream file("file.txt");
if (file)
file << i << " ";
}
}
int main()
{
cout << "WriteFile small" << endl;
WriteFile1(100000);
cout << "WriteFile big" << endl;
WriteFile2(1000000);
return 0;
}
Можно попытаться решить задачу без специальных инструментов, измерив время выполнения участков кода "вручную". Для этого фиксируется время начала и конца выполнения участка кода и разница между ними выводится на экран.
Но мы не хотим писать лишний код, поэтому используем профилировщик Visual Studio. Профилирование в Visual Studio можно выполнять с инструментированием и без. Под инструментированием подразумевается встраивание в исходный код специальных меток во время компиляции. По этим меткам профилировщик будет делать измерения. Главный недостаток здесь - нужна специальная сборка. Плюс программа становится громоздкой и немного "тормознутой".
Для профилирования без инструментирования достаточно отладочной сборки программы.Такая сборка, в общем-то, всегда под рукой и будет пошустрее инструментированной. Да, профилирование без инструментированиея выдает меньше данных и они могут быть не очень точные. Но практика показывает, что и этого часто достаточно.
Visual Studio позволяет профилировать приложения даже если они не разрабатываются в этой среде. Пусть это будет наш случай.
Запускаем Visual Studio и пропускаем экран приветствия.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702446946637/a5ba8807-7826-4be5-b349-da7240ca062b.png align="center")
Переходим к запуску профилировщика производительности.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702458065693/26271c5c-c441-4eaf-846d-4aa1e622dbcb.png align="center")
В качестве целевого объекта выбираем "Исполняемый файл".
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702458240762/c6c87ba7-b02a-4d61-a4f3-0e2afc88e794.png align="center")
Настраиваем параметры запуска исполняемого файла. Как минимум указываем путь к файлу. Рабочая директория пусть совпадает с директорией исполняемого файла.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702458772541/d072aefd-0c81-4621-a4ec-8a6ffc43502e.png align="center")
Теперь остается выбрать режим профилирования и можно запускать. "Использование ЦП" - наш случай. Профилирование без инструментирования.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702458927006/635af341-ec66-4657-90d7-05a82e0b6900.png align="center")
После нажатия на "Начать" будет запущена целевая программа под профилировщиком. По окончании работы программы профилировщик построит отчет.
В отчете приводится разная информация, но нам интересен критический путь. Критический путь отражает самый ресурсозатратный стек вызовов в программе. То есть в каких функциях программа проработала дольше всего. Критических путей может быть несколько. Это возможно, когда критические пути похожи по характеристикам друг с другом, либо в значительной степени выделяются на фоне остального стека.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702469762582/fb8b7759-d0ff-4078-845c-4bb1f5cc5245.png align="center")
Таблица критического пути содержит два параметра:
-
Общее время ЦП - это время, затраченное функцией вместе с ее внутренними вызовами других функций. (ЦП - центральный процессор).
-
Собственное время ЦП - это время, затраченное функцией, исключая ее внутренние вызовы других функций.
При клике на любой элемент стека критического пути открывается представление функций. Посмотрим данные по функции WriteFile2()
.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702470615312/1743b601-daea-4e94-bcc9-b0588fff051f.png align="center")
На представлении функций становится понятно, что проблема производительности связана со строкой открытия файла. Она занимает 84,48% всего времени выполнения программы. Предположим, что это связано с тем, что открытие файла выполняется на каждой итерации цикла. Слишком часто...
Попробуем вынести код открытия файла за пределы цикла и снова запустим профилирование, чтобы убедиться в правильности решения.
#include <iostream>
#include <fstream>
using namespace std;
void WriteFile1(int n)
{
fstream file("file.txt");
if (file)
for (int i = 0; i < n; ++i)
file << i << " ";
}
void WriteFile2(int n)
{
fstream file("file.txt");
if (file)
for (int i = 0; i < n; ++i)
file << i << " ";
}
int main()
{
cout << "WriteFile small" << endl;
WriteFile1(100000);
cout << "WriteFile big" << endl;
WriteFile2(1000000);
return 0;
}
Повторное профилирование показало... Ничего не показало. Программа выполнилась настолько быстро, что профилировщик не зафиксировал время выполнения вложенных функций. Общее время ЦП стало равным 3 против 6159 перед оптимизацией. Задача решена.
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1702471125466/f3873461-27d2-4e11-adf4-d439eaba8d2c.png align="center")
Если есть необходимость в точных измерениях времени выполнения функций, а также подсчета количества их вызовов, то без способа с инструментированием не обойтись. В остальных случаях подойдет рассмотренный метод.
Телеграм: Так себе программист