Які інші програми роблять те саме, що і 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 визначає деякі академічні установи, які навчають (або, схоже,):
Берклі бу Клемсон Колорадо герцог Earlham fsu Indiana mit msu ncsa.illinois ncsu nyu ou Princeton psu Stanford ucsd umd umich utah utexas utk wustl
** За винятком інших способів вимагати виконання роботи, які не залишають сліду, який пояснює , чому , наприклад, шляхом опублікування повідомлення.
Оскільки я тут нічого не бачив, 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;
}
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/, якщо ви хочете відстежувати високу ефективність