Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Оптимизация кода сокращает время вычислений и затраты. В этом примере показано, как использовать средства профилирования Visual Studio для выявления и устранения проблем с производительностью в примере приложения .NET. Если вы хотите сравнить средства профилирования, см. Какой инструмент следует выбрать?
В этом руководстве рассматриваются следующие сведения:
- Использование средств профилирования Visual Studio для анализа и повышения производительности.
- Практические стратегии оптимизации использования ЦП, выделения памяти и взаимодействия с базами данных.
Применяйте эти методы, чтобы сделать собственные приложения более эффективными.
Пример оптимизации
Пример приложения .NET выполняет запросы к базе данных SQLite блогов и записей с помощью Entity Framework. Он выполняет множество запросов, имитируя реальный сценарий извлечения данных. Приложение основано на примере начала работы Entity Framework, но использует более крупный набор данных.
К ключевым проблемам производительности относятся:
- Высокая загрузка ЦП: неэффективные вычисления или задачи обработки увеличивают потребление ЦП и затраты.
- Неэффективное выделение памяти: плохое управление памятью приводит к чрезмерной сборке мусора и снижению производительности.
- Затраты на базу данных: неэффективные запросы и чрезмерные вызовы базы данных ухудшают производительность.
В этом примере используются средства профилирования Visual Studio для выявления и решения этих проблем, направленные на то, чтобы сделать приложение более эффективным и экономичным.
Вызов
Устранение этих проблем с производительностью включает в себя несколько проблем:
- Диагностика узких мест: выявление основных причин высокой нагрузки ЦП, памяти или базы данных требует эффективного использования средств профилирования и правильной интерпретации результатов.
- Ограничения знаний и ресурсов: профилирование и оптимизация требуют определенных навыков и опыта, которые могут быть недоступны не всегда.
Стратегический подход к сочетанию средств профилирования, технических знаний и тщательного тестирования является важным для преодоления этих проблем.
Стратегия
Ниже приведено высокоуровневое представление подхода в этом примере:
- Начните с трассировки использования ЦП с помощью средства использования ЦП Visual Studio. Средство использования ЦП Visual Studio — это хорошая отправная точка для исследований производительности.
- Сбор дополнительных трассировок для анализа памяти и базы данных:
- Используйте средство выделения объектов .NET для аналитики памяти.
- Используйте средство базы данных для проверки запросов и времени SQL.
Для сбора данных требуются следующие задачи:
- Установите для приложения релизную сборку.
- Выберите средство использования ЦП в профилировщике производительности (ALT+F2).
- В профилировщике производительности запустите приложение и соберите трассировку.
Проверка областей высокого использования ЦП
После сбора трассировки с помощью инструмента использования ЦП и загрузки её в Visual Studio, сначала проверьте первоначальную страницу отчета .diagsession, в которой показаны суммированные данные. Используйте ссылку Open details в отчете.
В представлении сведений отчета откройте представление дерева вызовов . Путь кода с наибольшим использованием ЦП в приложении называется горячим путем. Значок пламени Hot Path () может помочь быстро определить проблемы с производительностью, которые можно улучшить.
В представлении дерева вызовов можно увидеть высокую загрузку ЦП для метода GetBlogTitleX
в приложении, используя около 60% общий объем использования ЦП приложения. Однако значение самообслуживания ЦП для GetBlogTitleX
низкое, только около 10%. В отличие от Всего ЦП, значение Собственного ЦП исключает время, затраченное на другие функции, поэтому нам нужно изучить более глубоко дерево вызовов, чтобы найти реальное узкое место.
GetBlogTitleX
выполняет внешние вызовы к двум LINQ DLL, которые используют большую часть времени процессора, как это видно по очень высоким значениям Сам процессор. Это первый ключ, что запрос LINQ может быть областью для оптимизации.
Чтобы получить визуальное дерево вызовов и другое представление данных, откройте представление диаграммы пламени. (Или щелкните правой кнопкой мыши GetBlogTitleX
и выберите Просмотр в Flame Graph.) Здесь снова похоже, что метод GetBlogTitleX
отвечает за значительное использование ЦП приложением (показано желтым цветом). Внешние вызовы библиотек DLL LINQ отображаются под полем GetBlogTitleX
, и они используют все время ЦП для метода.
Сбор дополнительных данных
Часто другие средства могут предоставить дополнительную информацию, чтобы помочь анализу и изолировать проблему. В этом примере мы рассмотрим следующий подход:
- Сначала рассмотрим использование памяти. Может существовать корреляция между высокой загрузкой ЦП и высоким использованием памяти, поэтому может быть полезно изучить оба аспекта для выявления проблемы.
- Поскольку мы определили библиотеки DLL LINQ, мы также рассмотрим инструмент работы с базами данных.
Проверка использования памяти
Чтобы узнать, что происходит с приложением с точки зрения использования памяти, мы собираем трассировку с помощью средства выделения объектов .NET (для C++, вы можете использовать средство использования памяти вместо него). В представлении дерева вызовов в трассировке памяти отображается горячий путь и помогает определить область с высоким использованием памяти. На этом этапе не удивительно, что метод GetBlogTitleX
, как представляется, создает много объектов! На самом деле более 900 000 выделений объектов.
Большинство созданных объектов — это строки, массивы объектов и Int32s. Мы можем увидеть, как эти типы создаются, проверив исходный код.
Проверка запроса в средстве "База данных"
В профилировщике производительности мы выбираем средство базы данных вместо использования ЦП (или выберите оба). Когда мы соберем трассировку, откройте вкладку Запросов на странице диагностики. На вкладке "Запросы" для трассировки базы данных отображается первая строка с самым длинным запросом 2446 мс. В столбце записей показано, сколько записей считывает запрос. Эти сведения можно использовать для последующего сравнения.
Проверив инструкцию SELECT
, созданную LINQ в столбце запроса, мы определяем первую строку как запрос, связанный с методом GetBlogTitleX
. Чтобы просмотреть полную строку запроса, разверните ширину столбца. Полная строка запроса:
SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"
Обратите внимание, что приложение извлекает здесь много значений столбцов, возможно, больше, чем нам нужно. Рассмотрим исходный код.
Оптимизация кода
Пришло время взглянуть на исходный код GetBlogTitleX
. В средстве базы данных щелкните правой кнопкой мыши запрос и выберите Перейти к исходному файлу. В исходном коде для GetBlogTitleX
мы находим следующий код, использующий LINQ для чтения базы данных.
foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
{
foreach (var post in blog.Posts)
{
if (post.Author == "Fred Smith")
{
Console.WriteLine($"Post: {post.Title}");
}
}
}
Этот код использует циклы foreach
для поиска базы данных для любых блогов с "Фред Смит" в качестве автора. Глядя на это, вы можете увидеть, что многие объекты создаются в памяти: новый массив объектов для каждого блога в базе данных, связанные строки для каждого URL-адреса и значения свойств, содержащихся в записях, таких как идентификатор блога.
Мы делаем небольшое исследование и находим некоторые распространенные рекомендации по оптимизации запросов LINQ. Кроме того, мы можем сэкономить время и позволить Copilot сделать исследования для нас.
Если мы используем Copilot, выберите Ask Copilot в контекстном меню и введите следующий вопрос:
Can you make the LINQ query in this method faster?
Совет
Для формирования хороших вопросов для Copilot можно использовать такие команды с косой чертой, как /optimize.
В этом примере Copilot предоставляет следующие предложенные изменения кода, а также объяснение.
public void GetBlogTitleX()
{
var posts = db.Posts
.Where(post => post.Author == "Fred Smith")
.Select(post => post.Title)
.ToList();
foreach (var postTitle in posts)
{
Console.WriteLine($"Post: {postTitle}");
}
}
Этот код содержит несколько изменений, которые помогут оптимизировать запрос:
- Добавлено предложение
Where
и устранён один из цикловforeach
. - В инструкции
Select
проецируется только свойство Title, и это все, что нам нужно в этом примере.
Затем мы повторно проверим, используя средства профилирования.
Результаты
После обновления кода мы повторно запускаем инструмент для измерения использования ЦП для сбора данных. В представлении дерева вызовов показано, что GetBlogTitleX
выполняется только 1754 мс, используя 37% всего ЦП приложения, значительно улучшился с 59%.
Переключитесь на представление графа пламени, чтобы увидеть другую визуализацию, демонстрирующую улучшение. В этом представлении GetBlogTitleX
также использует меньшую часть ЦП.
Проверьте результаты трассировки в инструменте базы данных, и только две записи были считаны с помощью этого запроса вместо 100 000! Кроме того, запрос значительно упрощается и устраняет ненужные ранее созданные левые соединения.
Затем мы заново проверяем результаты в средстве выделения объектов .NET и увидим, что GetBlogTitleX
отвечает только за выделение 56 000 объектов, что почти на 95% меньше, чем 900 000!
Итерировать
Может потребоваться несколько оптимизаций, и мы можем продолжить итерацию изменений кода, чтобы увидеть, какие изменения повышают производительность и помогают сократить затраты на вычисления.
Дальнейшие действия
В следующих статьях и блогах содержатся дополнительные сведения, которые помогут вам эффективно использовать средства производительности Visual Studio.
- Кейс-стадия : Изоляция проблемы с производительностью
- пример: двойная производительность в течение 30 минут
- Улучшение производительности Visual Studio с помощью нового средства инструментирования