Альтернативы gprof [закрыто]

166

Какие другие программы делают то же самое, что и gprof?

Нейромант
источник
2
какие платформы вас интересуют?
osgx
2
Я заинтересован в Linux.
нейромант
2
возможный дубликат stackoverflow.com/questions/375913/…
Dour High Arch
13
@ Грегори - Я склонен согласиться, и, возможно, он должен дать ответы на свои вопросы, 229 против 6, все 6 из этих ответов относятся к его собственным вопросам ...
Жан-Бернар Пеллерен
5
Как этот вопрос не может быть конструктивным?
JohnTortugo

Ответы:

73

В Valgrind есть профилировщик количества команд с очень хорошим визуализатором KCacheGrind . Как рекомендует Майк Данлавей, Вальгринд подсчитывает долю инструкций, для которых процедура находится в стеке, хотя мне жаль говорить, что она кажется запутанной при наличии взаимной рекурсии. Но визуализатор очень хорош и опережает светлые годы gprof.

Норман Рэмси
источник
2
@Norman: ++ Эта путаница с рекурсией кажется эндемичной для систем, которые имеют понятие распространения времени между узлами в графе. Также я думаю, что время настенных часов обычно более полезно, чем время команд ЦП, а строки кода (инструкции вызова) более полезны, чем процедуры. Если взяты образцы стека в случайное время настенных часов, то дробная стоимость линии (или процедуры, или любого другого описания, которое вы можете сделать) просто оценивается долей образцов, которые ее показывают.
Майк Данлавей
1
... Я подчеркиваю инструкции по вызову, но это относится к любым инструкциям. Если у кого-то есть узкое место горячей точки честности, например пузырьковая сортировка большого массива чисел, то инструкции сравнения / прыжка / обмена / приращения внутреннего цикла будут находиться в верхней / нижней части почти каждого образца стека. , Но (особенно когда программное обеспечение становится большим, и едва ли у любой подпрограммы есть много «собственного» времени), многие проблемы на самом деле являются инструкциями вызова, требующими работы, которая, когда становится ясно, сколько это стоит, на самом деле не должна выполняться.
Майк Данлавей
3
... Проверь это. Я думаю, что они почти на правильном пути: rotateright.com/zoom.html
Майк Данлавей
195

gprof (прочитайте статью) существует по историческим причинам. Если вы думаете, что это поможет вам найти проблемы с производительностью, это никогда не было объявлено как таковое. Вот что говорится в газете:

Профиль можно использовать для сравнения и оценки затрат на различные реализации.

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

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

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

Попробуйте это вместо этого.
Вот пример ускорения в 44 раза.
Вот ускорение в 730 раз.
Вот 8-минутная демонстрация видео.
Вот объяснение статистики.
Вот ответ на критику.

Есть простое наблюдение за программами. В данном исполнении каждая инструкция отвечает за некоторую долю общего времени (особенно callинструкций), в том смысле, что если бы ее не было, время не было бы потрачено. В течение этого времени инструкция находится в стеке **. Когда это понято, вы можете увидеть это -

gprof воплощает определенные мифы о производительности, такие как:

  1. эта выборка счетчика программы полезна.
    Это полезно только в том случае, если у вас есть ненужное узкое место горячей точки, такое как пузырьковая сортировка большого массива скалярных значений. Как только вы, например, измените его на сортировку с использованием сравнения строк, это все еще является узким местом, но выборка счетчика программы не увидит его, потому что теперь горячая точка находится в сравнении строк. С другой стороны, если это будет выборка расширенного счетчика программы (стека вызовов), точка, в которой вызывается сравнение строк, цикл сортировки, ясно отображается. Фактически, gprof был попыткой исправить ограничения выборки только для ПК.

  2. что функции синхронизации важнее захвата трудоемких строк кода.
    Причиной этого мифа является то, что gprof не смог захватить выборки стека, поэтому вместо этого он работает, подсчитывает их вызовы и пытается захватить граф вызовов. Однако после определения дорогостоящей функции вам все равно нужно заглянуть в нее и найти строки, отвечающие за время. Если бы были сэмплы стека, которые вам не нужно было бы просматривать, эти строки были бы на сэмплах. (Типичная функция может иметь от 100 до 1000 инструкций. Вызов функции - 1 инструкция, поэтому то, что находит дорогостоящие вызовы, на 2-3 порядка точнее.)

  3. что граф вызовов важен.
    Что нужно знать о программе, это не то, где она тратит свое время, а почему, Когда он тратит время на функцию, каждая строка кода в стеке дает одну ссылку в цепочке рассуждений о том, почему она существует. Если вы видите только часть стека, вы можете видеть только часть причины, поэтому вы не можете точно сказать, действительно ли это время необходимо. Что говорит график звонков? Каждая дуга говорит вам, что некоторая функция A находилась в процессе вызова некоторой функции B в течение некоторой доли времени. Даже если A имеет только одну такую ​​строку кода, вызывающую B, эта строка дает лишь небольшую часть причины. Если вам повезет, возможно, у этой линии плохая причина. Обычно вам нужно увидеть несколько одновременных строк, чтобы найти плохую причину, если она есть. Если A вызывает B в более чем одном месте, то это говорит вам еще меньше.

  4. эта рекурсия - сложная запутанная проблема.
    Это только потому, что gprof и другие профилировщики чувствуют необходимость генерировать граф вызовов и затем приписывать время узлам. Если у каждого есть сэмплы стека, временные затраты каждой строки кода, которая появляется на сэмплах, являются очень простым числом - доля сэмплов, на которых он находится. Если есть рекурсия, то данная строка может появляться более одного раза в образце. Не важно. Предположим, что образцы берутся каждые N мс, и линия появляется на F% из них (отдельно или нет). Если эту линию можно сделать не занимающей время (например, удалив ее или разветвив вокруг нее), тогда эти выборки исчезнут , и время будет сокращено на F%.

  5. эта точность измерения времени (и, следовательно, большое количество образцов) имеет важное значение.
    Подумай об этом на секунду. Если строка кода на 3 образцах из пяти, то если бы вы могли стрелять как лампочка, это примерно на 60% меньше времени, которое будет использовано. Теперь вы знаете, что если бы вы взяли 5 разных образцов, вы могли бы видеть их только 2 раза или целых 4. Так что 60% -ное измерение больше похоже на общий диапазон от 40% до 80%. Если бы это было только 40%, вы бы сказали, что проблему не стоит исправлять? Так какой смысл в точности времени, когда вы действительно хотите найти проблемы ? 500 или 5000 образцов могли бы измерить проблему с большей точностью, но не нашли бы ее более точно.

  6. что подсчет операторов или вызовов функций полезен.
    Предположим, вы знаете, что функция была вызвана 1000 раз. Можете ли вы сказать из этого, сколько времени это стоит? Вам также необходимо знать, сколько времени в среднем требуется, чтобы умножить его на счетчик и разделить на общее время. Среднее время вызова может варьироваться от наносекунд до секунд, поэтому один подсчет мало что говорит. Если есть выборки из стека, стоимость подпрограммы или какого-либо оператора составляет только часть выборок, на которых она находится. Эта доля времени - это то, что в принципе можно было бы сэкономить в целом, если бы можно было сделать так, чтобы рутина или утверждение не заняли много времени, поэтому это имеет самое прямое отношение к производительности.

  7. что выборки не должны быть взяты, когда они заблокированы
    . Причины этого мифа двояки: 1) что выборка с ПК бессмысленна, когда программа ожидает, и 2) озабоченность точной синхронизацией. Тем не менее, для (1) программа вполне может ожидать чего-то, о чем она просила, например, файлового ввода-вывода, который вам нужно знать , и какие образцы стека показывают. (Очевидно, вы хотите исключить выборки во время ожидания ввода данных пользователем.) Для (2), если программа ожидает просто из-за конкуренции с другими процессами, это, вероятно, происходит довольно случайным образом во время работы. Таким образом, хотя программа может занять больше времени, это не окажет большого влияния на статистику, которая имеет значение, процент времени, в течение которого операторы находятся в стеке.

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

  9. то, что образцы должны быть взяты с высокой частотой.
    Это происходит из-за идеи, что проблема производительности может быть быстродействующей, и что выборки должны быть частыми, чтобы ее решить. Но, если проблема стоит 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.)

  10. что вы пытаетесь найти в узком
    месте, как если бы только один. Рассмотрим следующую временную шкалу выполнения: vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
    она состоит из действительно полезной работы, представленной как .. Есть проблемы vWxYzс производительностью, занимающие 1/2, 1/4, 1/8, 1/16, 1/32 времени соответственно. Выборка находит vлегко. После удаления
    xWzWxWYWxW.WxWYW
    программа запускается вдвое дольше, а теперь Wзанимает половину времени, и ее легко найти. Он удаляется, оставляя
    xzxYx.xY
    этот процесс продолжающимся, каждый раз удаляя самую большую, в процентном отношении, проблему производительности, пока ничего не удастся найти. Теперь выполняется только то ., что выполняется в 1/32 от времени, использованного исходной программой. Это эффект увеличенияВ результате удаления любой проблемы остаток увеличивается в процентах, поскольку знаменатель уменьшается.
    Другим важным моментом является то, что должна быть найдена каждая отдельная проблема - ни одна из 5. не найдена. Любая проблема, не найденная и исправленная, значительно снижает конечный коэффициент ускорения. Просто найти некоторые, но не все, не достаточно хорошо.

ДОБАВЛЕНО: Я просто хотел бы указать на одну из причин, почему gprof популярен - его обучают, предположительно потому, что он бесплатный, прост в обучении и существует уже давно. Быстрый поиск в Google находит некоторые академические учреждения, которые преподают это (или, кажется, делают):

беркли бу клемсон колорадо герцог эрлхам фсу индия мит мсу нчса. иллинойс нсю нью оу принстон, псу стэнфорд великобритания умд умич юта утхас утк ути

** За исключением других способов запроса работы, которые не оставляют следа, объясняющего почему , например, путем отправки сообщения.

Майк Данлавей
источник
3
@ Норман: Я сделал профилировщик на основе этого, в C для DOS, около 93 года. Я назвал его еще одним анализатором производительности и продемонстрировал его на заседаниях IEEE, но это все, что нужно. Есть продукт от RotateRight под названием Zoom, который не так уж и далек. В * nix pstack подходит для этого вручную. Мой список дел для работы (фармакометрия на Windows) составляет около мили, что исключает веселые проекты, не говоря уже о семье. Это может быть полезно: stackoverflow.com/questions/1777669/…
Майк Данлавей
6
Я всегда считал, что профилировщики не так полезны для исправления медленного кода, и вместо этого использовал отборные биты кода отладки для измерения времени, затрачиваемого группой операторов по моему выбору, часто с помощью каких-то тривиальных небольших макросов или чего-то еще. Мне никогда не удавалось найти виновника, но меня всегда смущал мой подход «шкуры медведя и каменные ножи», когда «все остальные» (насколько я знаю) используют причудливые инструменты. Спасибо, что показали мне, почему я никогда не смог получить необходимую информацию от профилировщика. Это одна из самых важных идей, которые я видел на SO. Отлично сработано!
Уэйн Конрад
7
@osgx: я не хочу ничего копировать. Это как старый любимый автомобиль, простой и прочный, но есть вещи, которые он не делает, и мы должны осознавать это, и не только это, мы должны проснуться от мифов. Я понимаю, что на некоторых платформах может быть трудно получить образцы стеков, но если проблема такова, что gprof не найдет ее, то тот факт, что это единственный инструмент, не очень удобен.
Майк Данлавей
2
@Andrew: ... и если эта причина относится к некоторой значительной части выборок (например, более 1), то строка (ы) кода, которые могут устранить эту активность, находятся в этих выборках. График может дать вам подсказку об этом, но небольшое количество образцов стека просто покажет их вам.
Майк Данлавей
2
@Matt: Примеры проблем с производительностью ввода-вывода, которые можно найти следующим образом: 1) печать сообщений журнала в файл или консоль, которые ошибочно считали несущественными. 2) Преобразование между текстом и двойниками в числовом вводе-выводе. 3) Подземный ввод-вывод, извлекающий интернационализированные строки во время запуска, которые, как оказывается, не должны были интернационализироваться. Я натолкнулся на множество подобных примеров.
Майк Данлавей
63

Поскольку я не увидел здесь ничего относительно 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;
}
osgx
источник
Я только что проверил ваш пример и сделал 5 стеков. Вот что они нашли: 40% (примерно) времени f1звонили delete. 40% (примерно) времени process_requestзвонили delete. Большая часть оставшейся части была потрачена в new. Измерения грубые, но горячие точки точно определены.
Майк Данлавей,
Что это stackshot? Это что pstackвыходы?
2
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace, 1) Я думаю, что ваш метод бесполезен, когда вам нужно проанализировать проблемы с производительностью программы, запущенной на сервере вашего клиента. 2) Я не уверен, как вы применяете эту технику для получения информации о программе, имеющей множество потоков, которые обрабатывают различные запросы. Я имею в виду, когда общая картина довольно сложная.
2
Что касается № 1. Иногда клиенты звонят и говорят, что ваша программа работает медленно. Вы не можете сразу сказать это the problem is outside your code, не так ли? Так как вам может понадобиться некоторая информация, чтобы поддержать вашу точку зрения. В этой ситуации вам в какой-то момент может потребоваться профилирование вашего приложения. Вы не можете просто попросить своего клиента запустить GDB, нажать ^ C и получить стеки вызовов. Это была моя точка зрения. Это пример spielwiese.fontein.de/2012/01/22/… . У меня была эта проблема, и профилирование очень помогло.
2
Что касается № 2. Упрощение - это хороший подход, я согласен. Иногда это работает. Если проблема с производительностью возникает только на сервере клиента, и вы не можете воспроизвести их на своем сервере, тогда профили будут полезны.
21

Попробуйте OProfile . Это гораздо лучший инструмент для профилирования вашего кода. Я бы также предложил Intel VTune .

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

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

Anycorn
источник
2
Как также упоминалось в ответе valgrind, Zoom от RotateRight ( rotateright.com ) предоставляет намного более приятный интерфейс и позволяет удаленное профилирование.
JanePhanie
не понравился oprofile, это казалось бессистемным
Мэтт Джоунер
@ Матт какой-то конкретный момент?
Anycorn
Он не мог справиться с более чем 10-ю выполнениями до генерации статистических переполнений, вывод не был особенно полезен, а документация ужасна.
Мэтт Джоунер
1
@ Опрофиль: ARM, POWER, ia64, ...
Anycorn
8

Посмотрите на Sysprof .

Возможно, ваш дистрибутив уже есть.

Сорен Сандманн
источник
sysprof генерировал довольно бесполезный вывод, и его было трудно прочитать
Мэтт Джойнер,