Подтвердить что ты не робот

Как написать профайлер?

Я хотел бы знать, как написать профайлер? Какие книги и/или статьи рекомендуется? Кто-нибудь может мне помочь?

Кто-то уже сделал что-то подобное?

4b9b3361

Ответ 2

Поощряя много, не так ли:)

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

Итак, если yoyu просто хочет получить ответы, которые даст вам профилировщик, идите к кому-то другому. Если вы ищете интеллектуальный вызов, почему бы не написать письмо?

Я написал пару, для среды времени выполнения, которые годами стали неуместными.

Существует два подхода

  • добавление чего-то к каждой функции или другой важной точке, которая регистрирует время и где оно.

  • когда таймер идет регулярно и заглядывает, где находится программа.

Версия JVMPI кажется первым видом - ссылка, предоставленная uzhin, показывает, что она может сообщать о целом ряде вещей (см. раздел 1.3). Что делает выполненные изменения, чтобы сделать это, поэтому профилирование может повлиять на производительность (и если вы профилируете то, что в противном случае было очень легким, но часто называемым функцией, оно может ввести в заблуждение).

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

Павел.

Ответ 3

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

Можно сказать, например, что команда I (обычно вызов функции) обходится вам в процентах X от общего времени выполнения, более или менее, так как она появляется в стеке на X% образцов.

Подумайте об этом, потому что это ключевой момент. Стек вызова существует до тех пор, пока программа запущена. Если конкретная команда вызова I находится в стеке X% времени, тогда, если эта инструкция может исчезнуть, то X% времени исчезнет. Это не зависит от того, сколько раз выполняется I или как долго выполняется вызов функции. Таким образом, таймеры и счетчики не имеют смысла. И в некотором смысле все инструкции являются инструкциями вызова, даже если они только вызывают микрокод.

Сэмплер основан на предпосылке, что лучше знать адрес инструкции I с точностью (потому что это то, что вы ищете), чем знать число X% с точностью. Если вы знаете, что вы могли бы сэкономить примерно 30% времени, перекодировав что-то, вы действительно заботитесь о том, что вы можете отключиться на 5%? Вы все еще захотите это исправить. Количество времени, которое оно фактически сохраняет, не будет сделано ни меньше, ни больше, если вы точно знаете X.

Таким образом, можно отбирать отсчеты с таймера, но, откровенно говоря, я счел нужным вызвать прерывание пользователем, нажимая обе клавиши сдвига в одно и то же время. Поскольку 20 образцов, как правило, много, и таким образом вы можете быть уверены, что возьмете образцы в соответствующее время (т.е. Не дожидаясь ввода пользователя), это было вполне адекватно. Другой способ состоял бы в том, чтобы делать только отсчеты по таймеру, пока пользователь удерживает обе клавиши сдвига (или что-то в этом роде).

Меня не беспокоило, что выборки могут замедлить работу программы, потому что целью было не измерять скорость, а находить самые дорогостоящие инструкции. После фиксации чего-то общее ускорение легко измерить.

Главное, что предоставил профайлер, был пользовательский интерфейс, чтобы вы могли безболезненно исследовать результаты. То, что выходит из фазы выборки, представляет собой набор проб стека вызовов, где каждый образец представляет собой список адресов инструкций, где каждая команда, а последняя - команда вызова. Пользовательский интерфейс был в основном тем, что называется "видом бабочки". Он имеет текущий "фокус", который является конкретной инструкцией. Слева отображается инструкция вызова непосредственно над этой инструкцией, которая отбирается из образцов стека. Если команда фокуса является инструкцией по вызову, то нижеприведенные ниже инструкции отображаются справа от образцов. На инструкции фокуса отображается процент, который представляет собой процент стеков, содержащих эту инструкцию. Аналогично для каждой команды слева или справа процент разбивается на частоту каждой такой команды. Конечно, инструкция была представлена ​​файлом, номером строки и именем функции, в которой она находилась. Пользователь мог легко изучить данные, щелкнув любую из инструкций, чтобы сделать его новым фокусом.

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

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

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

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

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

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

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

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

То, что часто пропущено, когда речь идет о профилировании, заключается в том, что вы можете сделать это несколько раз, чтобы найти несколько проблем. Например, предположим, что команда I1 находится в стеке 5% времени, а I2 находится в стеке 50% времени. Двадцать образцов легко найдут I2, но, возможно, не I1. Таким образом, вы исправляете I2. Затем вы делаете все это снова, но теперь I1 занимает 10% времени, поэтому 20 образцов, вероятно, увидят это. Этот эффект увеличения позволяет повторить применение профилирования для достижения больших усугубляемых факторов ускорения.

Ответ 5

В качестве другого ответа я просто посмотрел на LukeStackwalker на sourceforge. Это хороший, маленький пример сэмплера стека и хорошее место для начала, если вы хотите написать профайлер.

Здесь, на мой взгляд, это то, что он делает правильно:

  • Он отображает весь стек вызовов.

Вздох... так близко пока. Здесь IMO - это то, что он (и другие сэмплеры стека, такие как xPerf):

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

  • Не нужно брать так много образцов, если их хранить для хранения - проблема. Так как типичные проблемы с производительностью стоят от 10% до 90%, 20-40 образцов покажут их достаточно надежно. Сотни образцов дают больше точности измерений, но они не увеличивают вероятность обнаружения проблем.

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

    5/20 MyFile.cpp: 326 для (i = 0; я < strlen (s); ++ i)

Это говорит о том, что строка 326 в MyFile.cpp появилась на 5 из 20 образцов в процессе вызова strlen. Это очень важно, потому что вы можете сразу увидеть проблему, и знаете, сколько ускорений вы можете ожидать от ее исправления. Если вы замените strlen(s) на s[i], он больше не будет тратить время на этот вызов, поэтому эти образцы не будут возникать, а ускорение будет приблизительно 1/(1-5/20) = 20/(20- 5) = 4/3 = ускорение на 33%. (Спасибо Дэвиду Торнли за этот пример кода.)

  • Пользовательский интерфейс должен иметь представление "бабочка", показывающее утверждения. (Если в нем также отображаются функции, это нормально, но утверждения действительно имеют значение.) Например:

    3/20 MyFile.cpp: 502 MyFunction (myArgs)
    2/20 HisFile.cpp: 113 MyFunction (hisArgs)

    5/20 MyFile.cpp: 326 для (i = 0; я < strlen (s); ++ i)

    5/20 strlen.asm: 23... некоторый код сборки...

В этом примере строка, содержащая оператор for, является "фокусом внимания". Это произошло на 5 образцах. Две строки над ним говорят, что на 3 из этих образцов он был вызван из MyFile.cpp:502, а на 2 из этих образцов он был вызван из HisFile.cpp:113. Строка ниже этого говорит о том, что на всех 5 этих образцах она находилась в strlen (неудивительно). В общем, линия фокусировки будет иметь дерево "родителей" и дерево "детей". Если по какой-то причине линия фокусировки не является чем-то, что вы можете исправить, вы можете идти вверх или вниз. Цель состоит в том, чтобы найти строки, которые вы можете исправить, на как можно большем количестве образцов.

ВАЖНО: Профилирование не следует рассматривать как нечто, что вы делаете один раз. Например, в примере выше мы получили ускорение 4/3, установив одну строку кода. Когда процесс повторяется, другие проблемные строки кода должны отображаться на частоте 4/3, которую они делали раньше, и, следовательно, их легче найти. Я никогда не слышал о том, чтобы люди говорили об итерации процесса профилирования, но крайне важно получить общие большие усугубляемые ускорения.

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