Альтернативы gprof

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

Ответы

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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

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

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

  • что функции синхронизации важнее, чем захват длительных строк кода.
    Причиной этого мифа является то, что 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 обнаруживает некоторые академические учреждения, которые учат его (или появляются):

berkeley bu clemson Колорадо Герцог ncsa.illinois ncsu nyu ou princeton psu Стэнфорд ucsd umd umich utah utexas utk wustl

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

Ответ 3

Поскольку я ничего не видел о 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

Затем я анализирую нагрузку на модуль:

perf report --stdio -g none --sort comm, dso -i./my_test.perf.data​​p >

# 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]

Затем анализируется загрузка каждой функции:

perf report --stdio -g none -i./my_test.perf.data | С++ ФИЛТР

# 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 ...

Затем анализируются цепи вызовов:

perf report --stdio -g graph -i./my_test.perf.data | С++ ФИЛТР

# 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;
}

Ответ 4

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

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

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

Ответ 6

Взгляните на Sysprof.

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

Ответ 7

http://lttng.org/, если вы хотите высокопроизводительный трассировщик