Какие другие программы делают то же самое, что и gprof?
Какие другие программы делают то же самое, что и gprof?
Ответы:
В Valgrind есть профилировщик количества команд с очень хорошим визуализатором KCacheGrind . Как рекомендует Майк Данлавей, Вальгринд подсчитывает долю инструкций, для которых процедура находится в стеке, хотя мне жаль говорить, что она кажется запутанной при наличии взаимной рекурсии. Но визуализатор очень хорош и опережает светлые годы gprof
.
gprof (прочитайте статью) существует по историческим причинам. Если вы думаете, что это поможет вам найти проблемы с производительностью, это никогда не было объявлено как таковое. Вот что говорится в газете:
Профиль можно использовать для сравнения и оценки затрат на различные реализации.
В нем не говорится, что его можно использовать для определения различных реализаций, подлежащих оценке, хотя это подразумевает, что это может быть сделано при особых обстоятельствах:
особенно если обнаруживается, что небольшие части программы доминируют над временем ее выполнения.
А как насчет проблем, которые не так локализованы? Это не имеет значения? Не возлагайте на gprof ожидания , которые никогда на него не претендовали. Это всего лишь инструмент измерения и только операций с процессором.
Попробуйте это вместо этого.
Вот пример ускорения в 44 раза.
Вот ускорение в 730 раз.
Вот 8-минутная демонстрация видео.
Вот объяснение статистики.
Вот ответ на критику.
Есть простое наблюдение за программами. В данном исполнении каждая инструкция отвечает за некоторую долю общего времени (особенно call
инструкций), в том смысле, что если бы ее не было, время не было бы потрачено. В течение этого времени инструкция находится в стеке **. Когда это понято, вы можете увидеть это -
gprof воплощает определенные мифы о производительности, такие как:
эта выборка счетчика программы полезна.
Это полезно только в том случае, если у вас есть ненужное узкое место горячей точки, такое как пузырьковая сортировка большого массива скалярных значений. Как только вы, например, измените его на сортировку с использованием сравнения строк, это все еще является узким местом, но выборка счетчика программы не увидит его, потому что теперь горячая точка находится в сравнении строк. С другой стороны, если это будет выборка расширенного счетчика программы (стека вызовов), точка, в которой вызывается сравнение строк, цикл сортировки, ясно отображается. Фактически, gprof был попыткой исправить ограничения выборки только для ПК.
что функции синхронизации важнее захвата трудоемких строк кода.
Причиной этого мифа является то, что gprof не смог захватить выборки стека, поэтому вместо этого он работает, подсчитывает их вызовы и пытается захватить граф вызовов. Однако после определения дорогостоящей функции вам все равно нужно заглянуть в нее и найти строки, отвечающие за время. Если бы были сэмплы стека, которые вам не нужно было бы просматривать, эти строки были бы на сэмплах. (Типичная функция может иметь от 100 до 1000 инструкций. Вызов функции - 1 инструкция, поэтому то, что находит дорогостоящие вызовы, на 2-3 порядка точнее.)
что граф вызовов важен.
Что нужно знать о программе, это не то, где она тратит свое время, а почему, Когда он тратит время на функцию, каждая строка кода в стеке дает одну ссылку в цепочке рассуждений о том, почему она существует. Если вы видите только часть стека, вы можете видеть только часть причины, поэтому вы не можете точно сказать, действительно ли это время необходимо. Что говорит график звонков? Каждая дуга говорит вам, что некоторая функция A находилась в процессе вызова некоторой функции B в течение некоторой доли времени. Даже если A имеет только одну такую строку кода, вызывающую B, эта строка дает лишь небольшую часть причины. Если вам повезет, возможно, у этой линии плохая причина. Обычно вам нужно увидеть несколько одновременных строк, чтобы найти плохую причину, если она есть. Если A вызывает B в более чем одном месте, то это говорит вам еще меньше.
эта рекурсия - сложная запутанная проблема.
Это только потому, что gprof и другие профилировщики чувствуют необходимость генерировать граф вызовов и затем приписывать время узлам. Если у каждого есть сэмплы стека, временные затраты каждой строки кода, которая появляется на сэмплах, являются очень простым числом - доля сэмплов, на которых он находится. Если есть рекурсия, то данная строка может появляться более одного раза в образце.
Не важно. Предположим, что образцы берутся каждые N мс, и линия появляется на F% из них (отдельно или нет). Если эту линию можно сделать не занимающей время (например, удалив ее или разветвив вокруг нее), тогда эти выборки исчезнут , и время будет сокращено на F%.
эта точность измерения времени (и, следовательно, большое количество образцов) имеет важное значение.
Подумай об этом на секунду. Если строка кода на 3 образцах из пяти, то если бы вы могли стрелять как лампочка, это примерно на 60% меньше времени, которое будет использовано. Теперь вы знаете, что если бы вы взяли 5 разных образцов, вы могли бы видеть их только 2 раза или целых 4. Так что 60% -ное измерение больше похоже на общий диапазон от 40% до 80%. Если бы это было только 40%, вы бы сказали, что проблему не стоит исправлять? Так какой смысл в точности времени, когда вы действительно хотите найти проблемы ? 500 или 5000 образцов могли бы измерить проблему с большей точностью, но не нашли бы ее более точно.
что подсчет операторов или вызовов функций полезен.
Предположим, вы знаете, что функция была вызвана 1000 раз. Можете ли вы сказать из этого, сколько времени это стоит? Вам также необходимо знать, сколько времени в среднем требуется, чтобы умножить его на счетчик и разделить на общее время. Среднее время вызова может варьироваться от наносекунд до секунд, поэтому один подсчет мало что говорит. Если есть выборки из стека, стоимость подпрограммы или какого-либо оператора составляет только часть выборок, на которых она находится. Эта доля времени - это то, что в принципе можно было бы сэкономить в целом, если бы можно было сделать так, чтобы рутина или утверждение не заняли много времени, поэтому это имеет самое прямое отношение к производительности.
что выборки не должны быть взяты, когда они заблокированы
. Причины этого мифа двояки: 1) что выборка с ПК бессмысленна, когда программа ожидает, и 2) озабоченность точной синхронизацией. Тем не менее, для (1) программа вполне может ожидать чего-то, о чем она просила, например, файлового ввода-вывода, который вам нужно знать , и какие образцы стека показывают. (Очевидно, вы хотите исключить выборки во время ожидания ввода данных пользователем.) Для (2), если программа ожидает просто из-за конкуренции с другими процессами, это, вероятно, происходит довольно случайным образом во время работы. Таким образом, хотя программа может занять больше времени, это не окажет большого влияния на статистику, которая имеет значение, процент времени, в течение которого операторы находятся в стеке.
что «собственное время» имеет значение «
Само время» имеет смысл только в том случае, если вы измеряете на уровне функции, а не на уровне линии, и вам кажется, что вам нужна помощь в определении того, идет ли функция времени в чисто локальных вычислениях, а не в вызываемых подпрограммах. При суммировании на уровне строки, строка представляет собой собственное время, если она находится в конце стека, в противном случае она представляет инклюзивное время. В любом случае, это стоит процент выборок стека, на котором он находится, так что в любом случае он будет найден для вас.
то, что образцы должны быть взяты с высокой частотой.
Это происходит из-за идеи, что проблема производительности может быть быстродействующей, и что выборки должны быть частыми, чтобы ее решить. Но, если проблема стоит 20%, скажем, из общего времени выполнения 10 секунд (или что-то еще), то каждая выборка за это общее время будет иметь 20% -ную вероятность ее попадания, независимо от того, возникает ли проблема в одном фрагменте, подобном этому
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 сэмплов, 4 попадания)
или во многих небольших фрагментах, подобных этому
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 сэмплов, 3 попадания). В
любом случае, число попаданий будет составлять в среднем примерно 1 к 5, независимо от того, сколько образцов будет взято, или как мало (Среднее = 20 * 0,2 = 4. Стандартное отклонение = +/- sqrt (20 * 0,2 * 0,8) = 1,8.)
что вы пытаетесь найти в узком
месте,
как если бы только один. Рассмотрим следующую временную шкалу выполнения: vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
она состоит из действительно полезной работы, представленной как .
. Есть проблемы vWxYz
с производительностью, занимающие 1/2, 1/4, 1/8, 1/16, 1/32 времени соответственно. Выборка находит v
легко. После удаления
xWzWxWYWxW.WxWYW
программа запускается вдвое дольше, а теперь W
занимает половину времени, и ее легко найти. Он удаляется, оставляя
xzxYx.xY
этот процесс продолжающимся, каждый раз удаляя самую большую, в процентном отношении, проблему производительности, пока ничего не удастся найти. Теперь выполняется только то .
, что выполняется в 1/32 от времени, использованного исходной программой. Это эффект увеличенияВ результате удаления любой проблемы остаток увеличивается в процентах, поскольку знаменатель уменьшается.
Другим важным моментом является то, что должна быть найдена каждая отдельная проблема - ни одна из 5. не найдена. Любая проблема, не найденная и исправленная, значительно снижает конечный коэффициент ускорения. Просто найти некоторые, но не все, не достаточно хорошо.
ДОБАВЛЕНО: Я просто хотел бы указать на одну из причин, почему gprof популярен - его обучают, предположительно потому, что он бесплатный, прост в обучении и существует уже давно. Быстрый поиск в Google находит некоторые академические учреждения, которые преподают это (или, кажется, делают):
беркли бу клемсон колорадо герцог эрлхам фсу индия мит мсу нчса. иллинойс нсю нью оу принстон, псу стэнфорд великобритания умд умич юта утхас утк ути
** За исключением других способов запроса работы, которые не оставляют следа, объясняющего почему , например, путем отправки сообщения.
Поскольку я не увидел здесь ничего относительно perf
относительно нового инструмента для профилирования ядра и пользовательских приложений в Linux, я решил добавить эту информацию.
Прежде всего - это учебник о профилировании Linux сperf
Вы можете использовать, perf
если ваше ядро Linux больше 2.6.32 или oprofile
оно старше. Обе программы не требуют от вас инструментов вашей программы (как gprof
требуется). Однако для того, чтобы правильно получить граф вызовов, perf
вам нужно собрать свою программу с помощью -fno-omit-frame-pointer
. Например: g++ -fno-omit-frame-pointer -O2 main.cpp
.
Вы можете увидеть «живой» анализ вашего приложения с perf top
:
sudo perf top -p `pidof a.out` -K
Или вы можете записать данные о производительности запущенного приложения и проанализировать их после этого:
1) Для записи данных о производительности:
perf record -p `pidof a.out`
или для записи в течение 10 секунд:
perf record -p `pidof a.out` sleep 10
или для записи с графом вызовов ()
perf record -g -p `pidof a.out`
2) проанализировать записанные данные
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
Или вы можете записать данные о производительности приложения и проанализировать их после этого, просто запустив приложение таким образом и ожидая его завершения:
perf record ./a.out
Это пример профилирования тестовой программы
Тестовая программа находится в файле main.cpp (я поставлю main.cpp внизу сообщения):
Я компилирую это так:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
Я использую, libmalloc_minimial.so
так как он скомпилирован, в -fno-omit-frame-pointer
то время как libc malloc, кажется, компилируется без этой опции. Затем я запускаю свою тестовую программу
./my_test 100000000
Затем я записываю данные о производительности запущенного процесса:
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
Затем я анализирую нагрузку на модуль:
отчет о перфекте --stdio -g нет - сортировать комм., dso -i ./my_test.perf.data
# Overhead Command Shared Object
# ........ ....... ............................
#
70.06% my_test my_test
28.33% my_test libtcmalloc_minimal.so.0.1.0
1.61% my_test [kernel.kallsyms]
Затем анализируется нагрузка на функцию:
отчет о проверке --stdio -g нет -i ./my_test.perf.data | C ++ ФИЛТР
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
29.14% my_test my_test [.] f1(long)
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
9.44% my_test my_test [.] process_request(long)
1.01% my_test my_test [.] operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
0.13% my_test [kernel.kallsyms] [k] native_write_msr_safe
and so on ...
Затем цепочки вызовов анализируются:
отчет о выполнении --stdio -g graph -i ./my_test.perf.data | C ++ ФИЛТР
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
|
--- f2(long)
|
--29.01%-- process_request(long)
main
__libc_start_main
29.14% my_test my_test [.] f1(long)
|
--- f1(long)
|
|--15.05%-- process_request(long)
| main
| __libc_start_main
|
--13.79%-- f2(long)
process_request(long)
main
__libc_start_main
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
|
--- operator new(unsigned long)
|
|--11.44%-- f1(long)
| |
| |--5.75%-- process_request(long)
| | main
| | __libc_start_main
| |
| --5.69%-- f2(long)
| process_request(long)
| main
| __libc_start_main
|
--3.01%-- process_request(long)
main
__libc_start_main
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
|
--- operator delete(void*)
|
|--9.13%-- f1(long)
| |
| |--4.63%-- f2(long)
| | process_request(long)
| | main
| | __libc_start_main
| |
| --4.51%-- process_request(long)
| main
| __libc_start_main
|
|--3.05%-- process_request(long)
| main
| __libc_start_main
|
--0.80%-- f2(long)
process_request(long)
main
__libc_start_main
9.44% my_test my_test [.] process_request(long)
|
--- process_request(long)
|
--9.39%-- main
__libc_start_main
1.01% my_test my_test [.] operator delete(void*)@plt
|
--- operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
|
--- operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
and so on ...
Итак, на данный момент вы знаете, где ваша программа проводит время.
И это main.cpp для теста:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t f1(time_t time_value)
{
for (int j =0; j < 10; ++j) {
++time_value;
if (j%5 == 0) {
double *p = new double;
delete p;
}
}
return time_value;
}
time_t f2(time_t time_value)
{
for (int j =0; j < 40; ++j) {
++time_value;
}
time_value=f1(time_value);
return time_value;
}
time_t process_request(time_t time_value)
{
for (int j =0; j < 10; ++j) {
int *p = new int;
delete p;
for (int m =0; m < 10; ++m) {
++time_value;
}
}
for (int i =0; i < 10; ++i) {
time_value=f1(time_value);
time_value=f2(time_value);
}
return time_value;
}
int main(int argc, char* argv2[])
{
int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
time_t time_value = time(0);
printf("number loops %d\n", number_loops);
printf("time_value: %d\n", time_value );
for (int i =0; i < number_loops; ++i) {
time_value = process_request(time_value);
}
printf("time_value: %ld\n", time_value );
return 0;
}
f1
звонили delete
. 40% (примерно) времени process_request
звонили delete
. Большая часть оставшейся части была потрачена в new
. Измерения грубые, но горячие точки точно определены.
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
, 1) Я думаю, что ваш метод бесполезен, когда вам нужно проанализировать проблемы с производительностью программы, запущенной на сервере вашего клиента. 2) Я не уверен, как вы применяете эту технику для получения информации о программе, имеющей множество потоков, которые обрабатывают различные запросы. Я имею в виду, когда общая картина довольно сложная.
the problem is outside your code
, не так ли? Так как вам может понадобиться некоторая информация, чтобы поддержать вашу точку зрения. В этой ситуации вам в какой-то момент может потребоваться профилирование вашего приложения. Вы не можете просто попросить своего клиента запустить GDB, нажать ^ C и получить стеки вызовов. Это была моя точка зрения. Это пример spielwiese.fontein.de/2012/01/22/… . У меня была эта проблема, и профилирование очень помогло.
Попробуйте OProfile . Это гораздо лучший инструмент для профилирования вашего кода. Я бы также предложил Intel VTune .
Два вышеупомянутых инструмента могут сузить время, затрачиваемое на конкретную строку кода, аннотировать ваш код, показать сборку и сколько времени занимает конкретная инструкция. Помимо метрики времени, вы также можете запросить конкретные счетчики, например, попадания в кеш и т. Д.
В отличие от gprof, вы можете профилировать любой процесс / двоичный файл, работающий в вашей системе, используя любой из двух.
Инструменты производительности Google включают простой в использовании профилировщик. Доступен как процессор, так и профилировщик кучи.
Посмотрите на Sysprof .
Возможно, ваш дистрибутив уже есть.
http://lttng.org/, если вы хотите высокопроизводительный трейсер