Руководство по оптимизации кода и снижению затрат на вычисления (C#, Visual Basic, C++, F#)

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

В этом примере рассматриваются следующие темы:

  • Важность оптимизации кода и ее влияние на снижение затрат на вычислительные ресурсы.
  • Как использовать средства профилирования Visual Studio для анализа производительности приложений.
  • Как интерпретировать данные, предоставляемые этими средствами, для выявления узких мест производительности.
  • Применение практических стратегий для оптимизации кода, акцент на использовании ЦП, выделении памяти и взаимодействии с базами данных.

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

Пример оптимизации

Пример приложения, рассмотренного в этом примере, — это приложение .NET, которое выполняет запросы к базе данных блогов и записей блогов. Он использует Entity Framework , популярный ORM (объектно-реляционное сопоставление) для .NET, для взаимодействия с локальной базой данных SQLite. Приложение структурировано для выполнения большого количества запросов, имитируя реальный сценарий, в котором приложение .NET может потребоваться для обработки обширных задач извлечения данных. Пример приложения — это измененная версия примера начала работы Entity Framework.

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

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

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

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

В этом примере рассматриваются эти проблемы, используя средства профилирования Visual Studio для анализа производительности приложения. Понимая, где и как можно улучшить производительность приложения, разработчики могут реализовать оптимизации, чтобы сократить использование ЦП, повысить эффективность распределения памяти, оптимизировать взаимодействие с базами данных и оптимизировать использование ресурсов. Конечная цель заключается в повышении общей производительности приложения, что делает его более эффективным и экономичным для выполнения.

Задача

Устранение проблем с производительностью в примере приложения .NET представляет несколько проблем. Эти проблемы связаны с сложностью диагностики узких мест производительности. Основные проблемы, связанные с устранением проблем, описанных ниже.

  • Диагностика узких мест производительности: одна из основных проблем заключается в точном определении первопричин проблем с производительностью. Высокая загрузка ЦП, неэффективное выделение памяти и затраты на взаимодействие с базой данных могут иметь несколько факторов. Разработчики должны эффективно использовать средства профилирования для диагностики этих проблем, что требует некоторого понимания того, как работают эти средства и как интерпретировать их выходные данные.

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

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

Стратегия

Ниже приведено высокоуровневое представление подхода в этом примере:

  • Мы начинаем расследование, принимая трассировку использования ЦП. Средство использования ЦП Visual Studio часто полезно для начала исследований производительности и оптимизации кода для снижения затрат.
  • Затем, чтобы получить дополнительные аналитические сведения, чтобы изолировать проблемы или повысить производительность, мы собираем трассировку с помощью одного из других средств профилирования. Например:
    • Мы рассмотрим использование памяти. Для .NET сначала мы пробуем средство выделения объектов .NET. (Для .NET или C++вместо этого можно просмотреть средство использования памяти.)
    • Для ADO.NET или Entity Framework можно использовать средство базы данных для изучения запросов SQL, точного времени запроса и многого другого.

Для сбора данных требуются следующие задачи:

  • Установка приложения на сборку выпуска.
  • Выбор средства использования ЦП в профилировщике производительности (ALT+F2). (Более поздние шаги включают несколько других инструментов.)
  • В профилировщике производительности запустите приложение и соберите трассировку.

Проверка областей высокого использования ЦП

После сбора трассировки с помощью средства использования ЦП и загрузки его в Visual Studio сначала проверьте начальную страницу отчета о диагсессии , в котором показаны суммированные данные. Используйте ссылку " Открыть сведения " в отчете.

Снимок экрана: открытие сведений об использовании ЦП.

В представлении сведений отчета откройте представление "Дерево вызовов". Путь кода с наибольшим использованием ЦП в приложении называется горячим путем. Значок пламени горячего пути (Снимок экрана: значок горячего пути.) может помочь быстро определить проблемы с производительностью, которые могут быть улучшены.

В представлении "Дерево вызовов" вы можете увидеть высокий уровень использования ЦП для GetBlogTitleX метода в приложении, используя около 60 % доли использования ЦП приложения. Тем не менее, значение самостоятельного ЦП для GetBlogTitleX низкого уровня, только около 10 %. В отличие от общего объема ЦП, значение самостоятельного ЦП исключает время, затраченное на другие функции, поэтому мы знаем, что мы знаем, чтобы изучить дерево вызовов для реального узких мест.

Снимок экрана: представление

GetBlogTitleX выполняет внешние вызовы к двум библиотекам DLL LINQ, которые используют большую часть времени ЦП, как показано на очень высоких значениях само ЦП . Это первый ключ, что запрос LINQ может быть областью для оптимизации.

Снимок экрана: представление

Чтобы получить визуальное дерево вызовов и другое представление данных, откройте представление "Диаграмма пламя". (Или щелкните правой кнопкой мыши и выберите GetBlogTitleX "Вид" в Режиме пламя.) Здесь снова похоже 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 и создания этого кода.

foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
  Console.WriteLine("Post: " + x);
}

В этом коде мы внесли несколько изменений, чтобы оптимизировать запрос:

  • Добавлено Where предложение и устранен один из foreach циклов.
  • Проецируется только свойство Title в инструкции Select , что все, что нам нужно в этом примере.

Затем мы повторно проверим профилирование с помощью средств профилирования.

Результаты

После обновления кода мы повторно запустите средство использования ЦП для сбора трассировки. В представлении "Дерево вызовов" показано, что GetBlogTitleX выполняется только 1754 мс, используя 37 % от общей суммы ЦП приложения, значительное улучшение с 59 %.

Снимок экрана: улучшенное использование ЦП в представлении

Перейдите в представление "Диаграмма пламя" , чтобы увидеть другую визуализацию, показывающую улучшение. В этом представлении GetBlogTitleX также используется меньшая часть ЦП.

Снимок экрана: улучшенное использование ЦП в представлении

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

Снимок экрана: быстрое время запроса в средстве

Затем мы перепроверим результаты в средстве выделения объектов .NET и видим, что GetBlogTitleX отвечает только за выделение объектов 56 000, почти на 95% сокращение от 900 000!

Снимок экрана: сокращение выделения памяти в средстве выделения объектов .NET.

Выполнение итерации

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

Следующие шаги

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