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

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

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

Запуск исследования

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

Пример сбора данных

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

Для сбора данных требуются следующие действия (не показаны здесь):

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

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

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

Вы также можете просмотреть горячий путь в представлении дерева вызовов. Чтобы открыть это представление, используйте ссылку "Открыть сведения " в отчете и выберите " Дерево вызовов".

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

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

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

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

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

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

Сбор дополнительных данных

Часто другие средства могут предоставить дополнительную информацию, чтобы помочь анализу и изолировать проблему. Например, так как мы определили библиотеки DLL LINQ, мы сначала рассмотрим средство базы данных. Этот инструмент можно выбрать с несколькими параметрами, а также использование ЦП. После сбора трассировки выберите вкладку "Запросы" на странице диагностика.

На вкладке "Запросы" для трассировки базы данных отображается первая строка с самым длинным запросом 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"

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

Чтобы узнать, что происходит с приложением с точки зрения использования памяти, соберите трассировку с помощью средства выделения объектов .NET (для C++, используйте средство использования памяти вместо него). В представлении "Дерево вызовов" в трассировке памяти отображается горячий путь и помогает определить область использования высокой памяти. Не удивительно на этом этапе, метод, как представляется, GetBlogTitleX создает много объектов! На самом деле более 900 000 выделений объектов.

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

Большинство созданных объектов — это строки, массивы объектов и int32. Вы можете увидеть, как эти типы создаются, проверив исходный код.

Оптимизировать код

Пришло время взглянуть на исходный GetBlogTitleX код. В средстве выделения объектов .NET щелкните метод правой кнопкой мыши и выберите "Перейти к исходному файлу". В исходном коде для мы находим следующий код 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.