Профилирование приложений .NET с помощью секундомера - PullRequest
11 голосов
/ 22 марта 2012

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

* бесплатно как на свободе, т.е. лицензия включает в себя коммерческие приложения.

РЕДАКТИРОВАТЬ: В ответ на те, кто сказал мне «купить профилировщик»,Я бы хотел, но если бы я мог потратить столько денег, я бы потратил их на что-то другое.Я пытался убедить своего босса, что профилировщик того стоит, но мне не повезло.Этот вопрос в основном основан на любопытстве.Я бы никогда не рассматривал секундомер как замену настоящему профилировщику.

У меня есть небольшое тестовое приложение (написанное на C #), которое измеряет различия в производительности при использовании секундомера для каждой строки.Код теста такой:

int n = 100;
BigInteger f = 1;
for (int i = n; i > 1; i--)
{
    f *= i;
}

Вот полный код: http://pastebin.com/AvbQmT32

У меня есть секундомер для каждой строки кода.Это мой «профилировщик».У меня также есть один секундомер для всей программы.Это мой «профилировщик профилировщика».

У меня есть программа, настроенная как режим выпуска, любой процессор (на компьютере x64) и оптимизации отключены.

Когда я запускаю программу с профилировщикомотключено, я получаю что-то вроде этого:

             Line             |  Ticks
------------------------------|----------
                              |
Total time:                   |       359

Когда я запускаю его с включенным профилировщиком, я получаю что-то вроде этого:

             Line             |  Ticks
------------------------------|----------
                              |
int n = 100;                  |         3
BigInteger f = 1;             |        12
for (int i = n; i > 1; i--)   |       325
{                             |
    f *= i;                   |       539
}                             |
                              |
Total time:                   |      1710
Stopwatch overhead:           |       831

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

Теперь не часто имеет смысл профилировать каждую строку программы, так как обычно она работаетлучше с подходом «разделяй и властвуй».Обычно вы можете начать с профилирования кусков кода и сузить круг проблем с производительностью.

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

Однако при использовании секундомеров все еще есть накладные расходы, особенно если вы используете много.

Итак, вопрос к вопросу:

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

Я ценю ваши отзывы.

Ответы [ 7 ]

4 голосов
/ 22 марта 2012

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

Есть, конечно, случаи, когда вам нужно , чтобы тоже иметь информацию. Но не используйте его во всех приложениях, но в одной части функции или отдельной функции. В этом случае StopWatch является лучшим выбором. Учтите, что StopWatch - это .NET Class, поэтому имеет , это даже минимальные накладные расходы. Вы должны смотреть не на абсолютные , а на относительные значения.

Надеюсь, это поможет.

2 голосов
/ 31 марта 2012

Перво-наперво: ваши результаты совсем не удивительны. Если бы вы использовали коммерческий профилировщик, вы бы увидели нечто подобное: ваша программа будет работать намного дольше, когда она профилируется, чем когда это не так. Чем более детализирован профиль профилировщика, тем дольше вы можете ожидать его выполнения. Если учесть, что такие операторы, как «i> 1» и «i--», вероятно, будут выполняться как инструкции с одним процессором, становится очевидным, почему профилирование времени выполнения конкретной строки может занять намного больше времени, чем выполнение самой строки.

Тот факт, что профилирование увеличивает общее время выполнения вашей программы, не должен быть проблемой; как уже упоминали несколько других, важно не абсолютное количество времени выполнения программы, а сравнение времени работы отдельных частей друг с другом, чтобы найти узкое место. Но есть и другая проблема. Секундомер будет использовать высокочастотный таймер из базовой ОС, если таковой имеется; но даже это не может быть достаточно высоким. Высокочастотный таймер на моем 64-битном Windows 7 i5-2400 (Quad Core 3,10 ГГц) работает с тактовой частотой 3 020 556 раз в секунду. Это звучит как много; но с такой скоростью процессор мог выполнять тысячу команд между тактами. Это означает, что если вы пытаетесь измерить время, которое требуется для выполнения одной инструкции, вы либо слишком сильно отстаете, либо слишком сильно.

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

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

2 голосов
/ 29 марта 2012

Вы пробовали смотреть на CInject: http://codeinject.codeplex.com/documentation? Он в основном внедряет код в DLL. Кажется, что из коробки есть мониторинг производительности, но он также позволяет вам создавать свой собственный код для внедрения. Кажется, идеально подходит для того, что вы пытаетесь сделать.

2 голосов
/ 22 марта 2012

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

Если вы разрабатываете код на уровне, на котором производительность фактически является проблемой, и вам нужно профилировать его, чтобы найти код, вызывающий сбой, тогда приобретитеИнструмент для выполнения этой задачи для вас будет более чем окупить себя за сэкономленное время.Я рекомендую профилировщик, который поставляется с Visual Studio Premium.

1 голос
/ 22 марта 2012

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

Тем не менее, несмотря на это, хотя секундомер имеет высокое разрешение, я думаю, что вы заметите некоторое времяошибка измерения вещей размером всего в одну строку кода.Например, 'int n = 100' - это одна строка в сборке, поэтому в техническом плане я бы подумал, что это будет всего 1 такт.Насколько сильно вы замечаете в своих числах ход от бега к бегу?Если вы получаете вариацию, которая составляет значительный процент от вашего среднего значения, тогда эта ценность на самом деле не дает вам такой большой информации.

0 голосов
/ 31 марта 2012

Если у вас Visual Studio Ultimate , попробуйте использовать Visual Studio Profiler. Это очень хорошо.

0 голосов
/ 29 марта 2012

Возможно, вам следует мыслить с точки зрения времени вызова метода профилирования, а не отдельных строк. В конце концов, вызов метода обычно представляет собой одну строку в коде вызова. Также интересным подходом является использование библиотеки журналов для регистрации входа и выхода из метода, например, следующим образом (используя log4net в качестве выбранного регистратора):

public string MyMethod(string arg)
{
    string result = null;

    if (logger.IsDebugEnabled) logger.Debug("->MyMethod(" + arg + ")");

    // do stuff
    result = "Hello world!";

    if (logger.IsDebugEnabled) logger.Debug("<-MyMethod(" + arg + ") = " + result);

    return result;
}

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

...