Статьи

Профилирование приложения с помощью Visual Studio — Распределение памяти

Я обсуждал возможности выборки ЦП и сбора данных инструментальных средств в предыдущих статьях, и теперь пришло время сравнить показатели производительности приложений, предназначенные для памяти.

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

Настройка

Это простое консольное приложение C # с единственным способом — Main. Весь код выполняется внутри этого метода, и не выполняется никаких вызовов внешних библиотек. Вот как это выглядит:

using System;
using System.IO;

namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string[] fileList = Directory.GetFiles(@"D:\Temporary");

foreach (string file in fileList)
{
Console.WriteLine("Getting bytes for " + file + "...");
Console.WriteLine("Bytes for " + file + ": " + File.ReadAllBytes(file).Length);
}

Console.Read();
}
}
}

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

Как видно из кода, который я здесь показываю, я ссылаюсь на путь, указывающий на папку с именем Temporary . Чтобы проверить это, я скопировал туда набор маленьких и не очень маленьких файлов (кучу больших текстурных файлов и фильм). И это в значительной степени все, что нужно для симуляции интенсивного потребления памяти.

Испытывать это — получать и анализировать результаты

Чтобы начать процесс, перейдите в « Анализ»> «Запустить мастер производительности» и выберите .NET Memory Allocation (Sampling) :

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

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

Там процессор довольно загружен для процесса, как я начал, и есть некоторые критические всплески. Но это не та метрика, которую я ищу.

Функции, выделяющие большую часть памяти

Первый индикатор, который нас интересует для этой сессии профилирования. Единственное, что в нем есть, это то, что он немного предвзят Метод ReadAllBytes, конечно, потребляет много памяти, но не 100%. Так почему же тогда процент такой высокий? Из-за того, что он должен был читать большой файл, он занимал столько памяти, что другие вызовы методов считались незначительными с точки зрения памяти. Чтобы продемонстрировать этот факт, я собираюсь удалить большой файл из папки и снова запустить процесс профилирования. Вот что я получил в конце концов:

Это выглядит более реалистично — хотя суммы действительно незначительны, они не равны нулю.

Если вы нажмете на один из методов, вы сможете просмотреть данные в более подробном представлении.

Прежде всего, вы можете непосредственно увидеть во фрагменте кода объем памяти, выделенный для конкретных вызовов. Самый дорогой звонок выделен красным. Теперь измените представление на Inclusive Allocations :

Теперь код выделен немного по-другому, кроме того, выделены две дополнительные строки:

Исходя из частоты выборки, вызов, который запросил наибольшее количество выделений, выделяется красным, следующий (по следующему наибольшему количеству выделений) выделяется ярко-желтым, а третий (также с одним из наибольшего числа выделения) выделяется более светлым оттенком желтого. Это эффективный способ визуально понять, какие части кода могут потребовать оптимизации.

Типы с наибольшим количеством выделенной памяти

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

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

Хотя объем памяти очень маленький, он снова отличается от нуля. Иногда такое округление может быть неудобным (когда вам необходимо точно знать объем выделенной памяти), но в общих случаях это избавляет разработчика от просмотра значений, подобных 0,0001 (учитывая пропорции, заданные другими типами данных).

Тем не менее, даже учитывая, что показанные значения могут быть 0,00, разработчик все еще может просматривать фактические значения, независимо от того, насколько они малы. Для этого нажмите на перечисленный тип.

Отображаемая таблица подробно описывает количество исключительных и включающих выделений (и байтов) для каждого типа данных, даже для тех, которых не было в начальном списке. Для строки (в первоначальном списке указано как 0,00%) мы видим, что было 17802 включительно (не включая конец) байта.

ПРИМЕЧАНИЕ. Эти значения одинаковы, поскольку во время выборки из памяти значения обобщаются в общую сумму типа внутри одного метода — в моем случае это был Main, и у меня не было внешних вызовов или назначений.

Для сравнения в массиве байтов используется 780 715 158 байт. В этом случае процент для строки будет 0,0022% . Причина округления значения до нуля здесь ясна.

Типы с большинством экземпляров

Если предыдущий индикатор был основан на количестве байтов, то этот основан на количестве распределений. Давайте посмотрим, что записано для текущей сессии:

Этот индикатор является автономным и не изменяется, чтобы увидеть видимые изменения, если я изменю условия работы приложения. В качестве примера метода, который генерирует несколько строковых экземпляров, которые влияют на этот индикатор, я могу назвать Console.WriteLine .

Вывод

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

Связанное Чтение

Управление памятью в .NET

Проверьте и оптимизируйте использование памяти вашей программой с помощью .NET Profiler API