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


166

Які інші програми роблять те саме, що і gprof?


2
які платформи вас цікавлять?
osgx

2
Мене цікавить Linux.
невроман

2
можливий дублікат stackoverflow.com/questions/375913/…
Dour High Arch

13
@Gregory - Я схильний погодитися, і, можливо, він повинен зробити свій власний відповідь, 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 визначає деякі академічні установи, які навчають (або, схоже,):

Берклі бу Клемсон Колорадо герцог Earlham fsu Indiana mit msu ncsa.illinois ncsu nyu ou Princeton psu Stanford ucsd umd umich utah utexas utk wustl

** За винятком інших способів вимагати виконання роботи, які не залишають сліду, який пояснює , чому , наприклад, шляхом опублікування повідомлення.


3
@Norman: Я створив на цьому профайлер, в C для DOS, близько 93 року. Я назвав це ще одним аналізатором продуктивності та демонстрував це на засіданнях IEEE, але це настільки, наскільки це пішло. Є продукт від RotateRight під назвою Zoom, який не надто далеко. На * nix, pstack добре робити це вручну. Мій список справ для роботи (фармакометрія в Windows) становить приблизно милю, що заважає веселим проектам, не кажучи вже про родинних. Це може бути корисно: stackoverflow.com/questions/1777669/…
Майк Данлаве

6
Я завжди знаходив профілі, не такі корисні для фіксації повільного коду, і натомість використовував вибіркові біти коду налагодження для вимірювання часу, зайнятого групою операторів на мій вибір, які часто допомагають деякими тривіальними маленькими макросами або будь-якими іншими. Мені ніколи не забиралося занадто багато часу, щоб знайти винуватця, але я завжди збентежився тим, що мої "ведмежі шкури та кам'яні ножі" підходять, коли "всі інші" (наскільки я знаю) використовують вигадливі інструменти. Дякую, що показали мені, чому я ніколи не можу отримати потрібну інформацію від профілера. Це одна з найважливіших ідей, яку я бачив на SO. Молодці!
Уейн Конрад

7
@osgx: Я нічого не хочу зірвати. Це як старий улюблений автомобіль, простий і міцний, але є речі, які він не робить, і нам потрібно знати про це, і не тільки це, нам потрібно прокинутися з міфів. Я розумію, що на деяких платформах може бути важко отримати зразки стека, але якщо проблема така, що gprof її не знайде, факт, що це єдиний інструмент, - це малий комфорт.
Майк Данлаве

2
@Andrew: ... і якщо ця причина стосується деякої значної частки зразків (як-от більше 1), то рядки (-и) коду, які могли б усунути цю активність, є на цих зразках. Графік може дати вам натяк на це, але не велика кількість зразків стека просто покаже їх вам.
Майк Данлаве

2
@Matt: Приклади проблем із продуктивністю IO знайшли такий спосіб: 1) друк журнальних повідомлень у файл або консоль, які помилково вважали незначними. 2) Перетворення між текстом і парним числом в числовий IO. 3) Підземний IO, який витягує інтернаціоналізовані рядки під час запуску, рядки, які, як виявляється, не потребували інтернаціоналізації. Я нагадав безліч таких прикладів.
Майк Данлаве

63

Оскільки я тут нічого не бачив, perfщо є відносно новим інструментом для профілювання ядра та користувацьких програм у Linux, я вирішив додати цю інформацію.

Перш за все - це підручник щодо профілювання Linuxperf

Ви можете використовувати, 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 none --сполучити комм, 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]

Потім аналізується навантаження на функцію:

perf report --stdio -g none -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 ...

Потім аналізуються ланцюги викликів:

perf report --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;
}

Я щойно запустив ваш приклад і зробив 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, ви можете профілювати будь-який процес / бінарний файл, що працює у вашій системі, використовуючи будь-який із двох.


2
Як уже згадувалося у відповіді вальгринд, Zoom від RotateRight ( rotateright.com ) забезпечує набагато приємніший інтерфейс і дозволяє віддалене профілювання.
JanePhanie

не сподобався опрофіл, це здавалося випадковим
Метт Столяр

@Matt якийсь конкретний момент?
Анікорн

Не вдалося впоратися з більш ніж 10-ма виконанням перед створенням переповнення статистики, вихід не був особливо корисним, а документація - жахливою.
Метт Столяр

1
@Tho OProfile: ARM, POWER, ia64, ...
Anycorn



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.