Як я можу профілювати код C ++, який працює в Linux?


1816

У мене є додаток C ++, що працює на Linux, який я в процесі оптимізації. Як я можу точно визначити, які області мого коду працюють повільно?


27
Якщо ви надасте більше даних про стек свого розвитку, ви можете отримати кращі відповіді. Є профілі від Intel та Sun, але ви повинні використовувати їх компілятори. Це варіант?
Назгоб

2
На нього вже відповіли за наступним посиланням: stackoverflow.com/questions/2497211/…
Капіль Гупта

4
Більшість відповідей є codeпрофільними. Однак пріоритетна інверсія, згладжування кешу, вміст ресурсів тощо можуть бути чинниками оптимізації та ефективності. Я думаю, що люди читають інформацію в мій повільний код . Поширені запитання посилаються на цю тему.
бездушний шум


3
Раніше я використовував pstack випадковим шляхом, більшу частину часу надрукував найтиповіший стек, де програма найбільше часу, отже, вказуючи на вузьке місце.
Жозе Мануель Гомес Альварес

Відповіді:


1406

Якщо ваша мета - використовувати профілер, використовуйте один із запропонованих.

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

Просто зупиніть це кілька разів, і кожен раз переглядайте стек дзвінків. Якщо є якийсь код, який витрачає певний відсоток часу, 20% або 50%, чи що завгодно, то це ймовірність, що ви впізнаєте його в акті на кожній вибірці. Отже, це приблизно відсоток зразків, на яких ви це побачите. Не потрібні освічені здогадки. Якщо у вас є здогадки щодо проблеми, це доведе або спростує її.

У вас можуть виникнути кілька проблем із продуктивністю різного розміру. Якщо ви очистите будь-яку з них, інші частини займуть більший відсоток і їх буде легше помітити на наступних переходах. Цей ефект збільшення при ускладненні кількох проблем може призвести до дійсно великих факторів прискорення.

Caveat : Програмісти, як правило, скептично ставляться до цієї методики, якщо вони самі не використовували її. Вони скажуть, що профілі надають вам цю інформацію, але це правда лише, якщо вони виберуть весь стек викликів, а потім дозволять вивчити випадковий набір зразків. (Підсумки - це те, де втрачено уявлення.) Графіки дзвінків не дають вам тієї самої інформації, оскільки

  1. Вони не підсумовуються на рівні інструкцій, і
  2. Вони дають заплутані підсумки за наявності рекурсії.

Вони також скажуть, що це працює лише на іграшкових програмах, коли насправді це працює на будь-якій програмі, і, здається, краще працює на більших програмах, тому що вони, як правило, мають більше проблем. Вони скажуть, що іноді знаходять речі, які не є проблемами, але це правда лише якщо ви щось бачите один раз . Якщо ви бачите проблему на декількох зразках, вона реальна.

PS Це також можна зробити для багатопотокових програм, якщо є спосіб збирати зразки стека викликів пулу потоків у певний момент часу, як це є в Java.

PPS Як груба загальність, чим більше шарів абстрагування у вашому програмному забезпеченні, тим більше шансів виявити, що це є причиною проблем із продуктивністю (і можливістю отримати швидкість).

Додано : Це може бути не очевидно, але техніка відбору стеків працює однаково добре за наявності рекурсії. Причина полягає в тому, що час, який було б збережено при видаленні інструкції, приблизний частці зразків, що містять її, незалежно від кількості разів, коли це може відбутися в межах вибірки.

Ще одне заперечення, яке я часто чую, таке: " Це зупиниться десь випадково, і воно пропустить справжню проблему ". Це випливає з попереднього розуміння того, що є справжньою проблемою. Ключовою властивістю проблем продуктивності є те, що вони не піддаються очікуванням. Відбір проб говорить про те, що щось є проблемою, і ваша перша реакція - це невіра. Це природно, але ви можете бути впевнені, якщо це виявить проблему, це реально, і навпаки.

Додано : Дозвольте зробити байєсівське пояснення того, як це працює. Припустимо, є якась інструкція I(дзвінок чи інше), яка знаходиться в стеці викликів деяку частину fчасу (і, таким чином, коштує стільки). Для простоти, припустимо, ми не знаємо, що fтаке, але припустимо, що це або 0,1, 0,2, 0,3, ... 0,9, 1,0, і попередня ймовірність кожної з цих можливостей дорівнює 0,1, тому всі ці витрати однаково вірогідні апріорі.

Тоді припустимо, що ми беремо всього 2 зразки стека, і ми бачимо інструкцію Iщодо обох зразків, призначену для спостереження o=2/2. Це дає нам нові оцінки частоти fв IВідповідно до цього:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.1    1     1             0.1          0.1            0.25974026
0.1    0.9   0.81          0.081        0.181          0.47012987
0.1    0.8   0.64          0.064        0.245          0.636363636
0.1    0.7   0.49          0.049        0.294          0.763636364
0.1    0.6   0.36          0.036        0.33           0.857142857
0.1    0.5   0.25          0.025        0.355          0.922077922
0.1    0.4   0.16          0.016        0.371          0.963636364
0.1    0.3   0.09          0.009        0.38           0.987012987
0.1    0.2   0.04          0.004        0.384          0.997402597
0.1    0.1   0.01          0.001        0.385          1

                  P(o=2/2) 0.385                

Останній стовпець говорить, що, наприклад, ймовірність того, що f> = 0,5 дорівнює 92%, порівняно з попереднім припущенням 60%.

Припустимо, попередні припущення різні. Припустимо, ми припустимо, що P(f=0.1)це 991 (майже визначено), а всі інші можливості майже неможливі (0,001). Іншими словами, наша попередня впевненість - Iце дешево. Тоді ми отримуємо:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.001  1    1              0.001        0.001          0.072727273
0.001  0.9  0.81           0.00081      0.00181        0.131636364
0.001  0.8  0.64           0.00064      0.00245        0.178181818
0.001  0.7  0.49           0.00049      0.00294        0.213818182
0.001  0.6  0.36           0.00036      0.0033         0.24
0.001  0.5  0.25           0.00025      0.00355        0.258181818
0.001  0.4  0.16           0.00016      0.00371        0.269818182
0.001  0.3  0.09           0.00009      0.0038         0.276363636
0.001  0.2  0.04           0.00004      0.00384        0.279272727
0.991  0.1  0.01           0.00991      0.01375        1

                  P(o=2/2) 0.01375                

Зараз він каже, що P(f >= 0.5)це на 26%, порівняно з попереднім припущенням на 0,6%. Тож Байєс дозволяє нам оновити нашу оцінку ймовірної вартості I. Якщо обсяг даних невеликий, це не говорить нам точно, яка вартість, а лише те, що вона досить велика, щоб її варто було виправити.

Ще один спосіб поглянути на це називається Правилом Речення . Якщо ви переверніть монету 2 рази, і вона піднімає голови обидва рази, що це говорить про ймовірне зважування монети? Відповідний спосіб відповісти - сказати, що це бета-версія з середнім значенням (number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%.

(Ключовим є те, що ми бачимось Iне один раз. Якщо ми бачимо його лише один раз, це не говорить нам багато, крім цього f> 0.)

Отже, навіть дуже невелика кількість зразків може нам багато чого розповісти про вартість інструкцій, які вона бачить. (І це буде бачити їх з частотою, в середньому, пропорційно їх вартості. Якщо nберуться зразки, і fце вартість, то Iбудуть з'являтися на nf+/-sqrt(nf(1-f))зразках. Приклад, n=10, f=0.3, тобто 3+/-1.4зразки.)


Додано : щоб зрозуміти різницю між вимірюванням та вибірковою вибіркою стеку:
зараз є профілі, які вибирають стек, навіть у настінні годинники, але виходить вимірювання (або гарячий шлях, або гаряче місце, з якого «вузьке місце» можна легко заховати). Те, що вони вам не показують (а вони легко могли), - це самі фактичні зразки. І якщо ваша мета - знайти вузьке місце, їх кількість, яку вам потрібно побачити, в середньому становить 2, поділену на частку часу, яке потрібно. Отже, якщо це займе 30% часу, це буде показувати в середньому 2 / .3 = 6,7 проби, а шанс, що 20 зразків покаже, це 99,2%.

Ось нестандартна ілюстрація різниці між вивченням вимірювань та вивченням зразків стека. Вузьким місцем може бути одна велика крапля, як ця, або численні маленькі, це не має ніякого значення.

введіть тут опис зображення

Вимірювання горизонтальне; це говорить про те, яку частину часу займають конкретні процедури. Відбір проб вертикальний. Якщо є якийсь спосіб уникнути того, що робить вся програма на той момент, і якщо ви бачите це на другому зразку , ви знайшли вузьке місце. Саме це має значення - бачити всю причину витраченого часу, а не лише скільки.


292
Це, в основному, пробірник відбору проб бідного чоловіка, що чудово, але ви ризикуєте отримати занадто малий розмір вибірки, який, можливо, дасть вам абсолютно хибні результати.
Crashworks

100
@Crash: Я не буду обговорювати частину "бідного чоловіка" :-) Це правда, що для статистичної точності вимірювання потрібно багато вибірок, але є дві суперечливі цілі - вимірювання та визначення проблеми. Я зосереджую увагу на останньому, для чого вам потрібна точність розташування, а не точність вимірювання. Так, наприклад, може бути середній стек, один виклик функції A (); це займає 50% часу, але це може бути в іншій великій функції B, поряд з багатьма іншими викликами на A (), які не є дорогими. Точні підсумки часу функціонування можуть бути підказкою, але кожен інший зразок стека точно визначить проблему.
Майк Данлаве

41
... світ, здається, вважає, що графік викликів, позначений числом дзвінків та / або середнім терміном, є досить хорошим. Це не так. І сумна частина - для тих, хто вибирає стек дзвінків, найкорисніша інформація знаходиться прямо перед ними, але вони викидають її в інтересах "статистики".
Майк Данлі

30
Я не хочу погоджуватися з вашою технікою. Зрозуміло, я дуже сильно покладаюся на пробовідбірники пробовідбірки. Я просто вказую, що є деякі інструменти, які роблять це автоматизовано зараз, що важливо, коли ви минули момент отримання функції від 25% до 15% і вам потрібно збити її з 1,2% до 0,6%.
Crashworks

13
-1: Ідеальна ідея, але якщо вам платять за роботу навіть в умовах, орієнтованих на помірковану ефективність, це марна трата часу. Скористайтеся справжнім профілером, щоб нам не довелося йти за вами та виправляти актуальні проблеми.
Сем Харвелл

583

Ви можете використовувати Valgrind із наступними параметрами

valgrind --tool=callgrind ./(Your binary)

Він створить файл під назвою callgrind.out.x. Потім ви можете скористатися kcachegrindінструментом для читання цього файлу. Це дасть вам графічний аналіз речей з результатами, на зразок яких рядків коштує скільки.


51
Valgrind є великим, але майте в виду , що це зробить вашу програму штопати повільно
NEVES

30
Перевірте також Gprof2Dot на дивовижний альтернативний спосіб візуалізації результатів. ./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
Себастьян

2
@neves Так, Valgrind просто не дуже корисний з точки зору швидкості для профілювання програм "gstreamer" та "opencv" в режимі реального часу.
Энтузиазмек

1
stackoverflow.com/questions/375913 / ... часткове soluton для випуску швидкості.
Tõnu Samuel

3
@Sebastian: gprof2dotзараз тут: github.com/jrfonseca/gprof2dot
Джон Цвінк

348

Я припускаю, що ви використовуєте GCC. Стандартним рішенням було б профілювання за допомогою gprof .

Обов’язково додайте -pgдо компіляції перед профілюванням:

cc -o myprog myprog.c utils.c -g -pg

Я ще не пробував цього, але чув хороші речі про google-perftools . Це, безумовно, варто спробувати.

Пов'язані питання тут .

Ще декілька модних слів, якщо gprofвони не справляються за вас: Valgrind , Intel VTune , Sun DTrace .


3
Я згоден, що gprof - це поточний стандарт. Однак лише зауваження, що Valgrind використовується для профілактики витоків пам’яті та інших аспектів, пов’язаних із пам’яттю ваших програм, а не для оптимізації швидкості.
Білл Ящірка

68
Білл, у наборі vaglrind ви можете знайти покликання та масив. Обидва дуже корисні для профільних програм
dario minonne

7
@ Bill-the-Lizard: Деякі коментарі до gprof : stackoverflow.com/questions/1777556/alternatives-to-gprof/…
Майк Данлаве

6
gprof -pg - це лише наближення профілювання ставок виклику. Він вставляє mcount call для відстеження, які функції викликають, які інші функції. Він використовує стандартний відбір проб на час для, е, часу. Потім він розподіляється разів, відібраний у функції foo () назад до абонентів foo (), у відповідність до нумерації викликів. Тому він не розрізняє дзвінки різної вартості.
Krazy Glew

1
З clang / clang ++ можна подумати про використання процесора gperftools CPU. Caveat: Я так не робив сам.
einpoklum

257

Новіші ядра (наприклад, останні ядра Ubuntu) поставляються з новими інструментами ' perf ' ( apt-get install linux-tools) AKA perf_events .

Вони поставляються з класичними профілями вибірки ( man-page ), а також з приголомшливим графіком часу !

Важливим є те, що ці інструменти можуть бути системним профілюванням, а не просто процесуванням процесів - вони можуть показувати взаємодію між потоками, процесами та ядром та дозволяти зрозуміти залежності планування та вводу / виводу між процесами.

Alt текст


12
Чудовий інструмент! Чи є в мене все-таки такий типовий вид "метелик", який починається зі стилю "main-> func1-> fun2"? Я не можу зрозуміти, що це ... perf reportздається, дає мені імена функцій з батьками дзвінків ... (так це начебто перевернутий вигляд метелика)
kizzx2

Буде, може perf показати часовий графік активності потоку; з доданою інформацією про номер CPU? Я хочу побачити, коли і який потік працює на кожному процесорі.
osgx

2
@ kizzx2 - можна використовувати gprof2dotі perf script. Дуже приємний інструмент!
тире

2
Навіть новіші ядра, такі як 4.13, мають eBPF для профілювання. Дивіться brendangregg.com/blog/2015-05-15/ebpf-one-small-step.html та brendangregg.com/ebpf.html
Ендрю Стерн

Ще одне приємне вступ perfіснує в archive.li/9r927#selection-767.126-767.271 (Чому боги SO вирішили видалити цю сторінку з бази знань SO поза мною ...)
ragerdl

75

Я б використовував Valgrind та Callgrind як основу для мого набору інструментів для профілювання. Важливо знати, що Valgrind - це віртуальна машина:

(wikipedia) Valgrind - це, по суті, віртуальна машина, що використовує способи компіляції, що вчасно (JIT), включаючи динамічну рекомпіляцію. Ніщо з оригінальної програми ніколи не запускається безпосередньо на хост-процесор. Натомість Valgrind спочатку переводить програму у тимчасову, більш просту форму під назвою «Проміжне представництво» (IR), яка є нейтральною для процесора формою на основі SSA. Після перетворення інструмент (див. Нижче) може зробити будь-які перетворення, які б хотіли на ІР, перш ніж Valgrind переведе ІК назад в машинний код і дозволить хост-процесору запустити його.

Callgrind - це профілер, який спирається на це. Основна перевага полягає в тому, що вам не доведеться запускати заявки годинами, щоб отримати надійний результат. Навіть одного другого пробігу достатньо для отримання надійних результатів, які є твердими, тому що Callgrind - це непрофілюючий профіль.

Інший інструмент, побудований на Валгрінд, - це масив. Я використовую його для профільного використання пам'яті. Це чудово працює. Це означає, що він дає вам знімки використання пам'яті - детальна інформація, ЩО містить вміст пам’яті, і хто її розмістив. Така інформація доступна в різні моменти запуску програми.


70

valgrind --tool=callgrindБез деяких варіантів відповідь провести не зовсім повно. Зазвичай ми не хочемо розміщувати 10 хвилин повільного часу запуску під Valgrind і хочемо профілювати нашу програму, коли вона виконує якесь завдання.

Тож ось що я рекомендую. Запустіть програму спочатку:

valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no ./binary > tmp

Тепер, коли це працює, і ми хочемо розпочати профілювання, нам слід запустити в іншому вікні:

callgrind_control -i on

Це призводить до профілювання. Щоб вимкнути його та зупинити ціле завдання, ми можемо використовувати:

callgrind_control -k

Тепер у нас є деякі файли з ім'ям callgrind.out. * У поточному каталозі. Щоб побачити результати профілювання, використовуйте:

kcachegrind callgrind.out.*

Я рекомендую в наступному вікні натиснути на заголовок стовпця "Самостійно", інакше це покаже, що "main ()" є найбільш трудомістким завданням. "Я" показує, скільки кожна функція потребувала часу, а не разом із залежними.


9
Тепер чомусь файли callgrind.out. * Завжди були порожніми. Виконання callgrind_control -d було корисним для примусового скидання даних на диск.
Tõnu Samuel

3
Не можу Мої звичні контексти - це щось на кшталт цілого MySQL або PHP або щось подібне. Часто навіть не знаю, що я хочу спочатку розділити.
Tõnu Samuel

2
Або в моєму випадку моя програма фактично завантажує купу даних у кеш LRU, і я не хочу цього робити. Тож я примушую завантажувати підмножину кешу при запуску та профілювати код, використовуючи лише ті дані (дозволяючи OS + CPU керувати використанням пам'яті в моєму кеше). Це працює, але завантаження цього кешу відбувається повільно і процесор інтенсивно застосовується в коді, який я намагаюсь профілювати в іншому контексті, тому callgrind дає погано забруднені результати.
Кодовий мерзотник

2
є також CALLGRIND_TOGGLE_COLLECTможливість вмикати / вимикати колекцію програмно; дивіться stackoverflow.com/a/13700817/288875
Андре Хольцнер

1
Нічого собі, я не знав, що це існує, дякую!
Вінсент Фурмонд

59

Це відповідь на відповідь Назгоба Gprof .

Я використовував Gprof останні пару днів і вже знайшов три значні обмеження, одного з яких я ще не бачив документально ніде (поки):

  1. Він не працює належним чином у багатопотоковому коді, якщо ви не використовуєте обхідне рішення

  2. Графік виклику плутається за функціональними вказівниками. Приклад: У мене є функція, multithread()яка називається, що дозволяє мені багатопотоково задавати функцію через визначений масив (обидва передані як аргументи). Однак Gprof розглядає всі дзвінки multithread()як рівнозначні для обчислення часу, проведеного у дітей. Оскільки деякі функції, які я передаю, multithread()займають набагато більше часу, ніж інші, мої графіки викликів здебільшого марні. (Для тих, хто цікавиться, чи не проблема тут: ни, multithread()може, необов'язково, і в цьому випадку, запускати все послідовно лише на викликову нитку).

  3. Він каже тут , що «... цифри ЧИСЛО-викликів отримані шляхом підрахунку, чи не пробуючи. Вони абсолютно точні ...». Але я знаходжу свою графіку викликів, яка дає мені 5345859132 + 784984078 як статистику викликів для моєї найбільш називаної функції, де перший номер повинен бути прямим викликом, а другий рекурсивними дзвінками (які всі самі по собі). Оскільки це означало, що у мене виникла помилка, я вводив у код довгі (64-бітні) лічильники і робив те ж саме запуск знову. Моя кількість: 5345859132 прямі та 78094395406 саморекурсивні дзвінки. Існує багато цифр, тому я зазначу, що рекурсивні дзвінки, за якими я вимірюю, складають 78 мільярдів проти 784 м від Gprof: коефіцієнт 100 різних. Обидва запуски були однопоточним та неоптимізованим кодом, один складений -gта інший -pg.

Це GNU Gprof (GNU Binutils для Debian) 2.18.0.20080103, який працює під 64-розрядним Debian Lenny, якщо це комусь допомагає.


Так, це робить вибірку, але не для цифр про кількість дзвінків. Цікаво, що перегляд вашого посилання врешті-решт привів мене до оновленої версії сторінки керівництва, на яку я посилався у своєму дописі, нова URL-адреса: sourceware.org/binutils/docs/gprof/… Це повторює цитату в частині (iii) моєї відповіді, але також говорить: "У багатопотокових програмах або однопотокових програмах, які пов'язуються з багатопотоковими бібліотеками, підрахунки є лише детермінованими, якщо функція підрахунку є безпечною для потоків. -сейф)."
Rob_before_edits

Мені незрозуміло, чи це пояснює мій результат у (iii). Мій код був пов’язаний -lpthread -lm і оголосив як "pthread_t * thr", так і "pthread_mutex_t nextLock = PTHREAD_MUTEX_INITIALIZER" статичною змінною, навіть коли вона працює з однопотоковою ниткою. Я б звичайно припускав, що "зв'язок з багатопотоковими бібліотеками" означає фактично використання цих бібліотек, і більшою мірою, ніж це, але я можу помилятися!
Rob_before_edits

23

Використовуйте Valgrind, callgrind та kcachegrind:

valgrind --tool=callgrind ./(Your binary)

створює callgrind.out.x. Прочитайте його, використовуючи kcachegrind.

Використовуйте gprof (add -pg):

cc -o myprog myprog.c utils.c -g -pg 

(не дуже добре для багатопотокових, функціональних покажчиків)

Використовуйте google-perftools:

Виявлено використання часу вибірки, виявлено вузькі місця вводу / виводу та процесора.

Intel VTune - найкращий (безкоштовний для навчальних цілей).

Інші: AMD Codeanalyst (оскільки замінено AMD CodeXL), OProfile, інструменти 'perf' (apt-get install linux-tools)


10

Огляд методик профілювання C ++

У цій відповіді я використаю кілька різних інструментів для аналізу кількох дуже простих тестових програм, щоб конкретно порівняти, як ці інструменти працюють.

Наступна програма тестування дуже проста і робить наступне:

  • mainдзвінки fastі maybe_slow3 рази, один з maybe_slowдзвінків повільний

    Повільний виклик на maybe_slow10 разів довший і домінує під час виконання, якщо врахувати дзвінки до дочірньої функції common. В ідеалі інструмент профілювання зможе вказати на конкретний повільний дзвінок.

  • як fastі maybe_slowвиклик common, на частку якого припадає основна частина виконання програми

  • Інтерфейс програми:

    ./main.out [n [seed]]

    і програма робить O(n^2)циклі в цілому. seedце просто отримати різний вихід, не впливаючи на час виконання.

main.c

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __attribute__ ((noinline)) common(uint64_t n, uint64_t seed) {
    for (uint64_t i = 0; i < n; ++i) {
        seed = (seed * seed) - (3 * seed) + 1;
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) fast(uint64_t n, uint64_t seed) {
    uint64_t max = (n / 10) + 1;
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) maybe_slow(uint64_t n, uint64_t seed, int is_slow) {
    uint64_t max = n;
    if (is_slow) {
        max *= 10;
    }
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

int main(int argc, char **argv) {
    uint64_t n, seed;
    if (argc > 1) {
        n = strtoll(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    if (argc > 2) {
        seed = strtoll(argv[2], NULL, 0);
    } else {
        seed = 0;
    }
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 1);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    printf("%" PRIX64 "\n", seed);
    return EXIT_SUCCESS;
}

гпроф

gprof вимагає перекомпілювати програмне забезпечення за допомогою інструментарію, а також використовує підхід вибірки разом з цим приладом. Тому він забезпечує баланс між точністю (вибірка не завжди є повністю точною і може пропустити функції) та сповільненням виконання (інструментальне та відбір проб є відносно швидкими методами, які не дуже сповільнюють виконання).

gprof вбудований у GCC / binutils, тому все, що нам потрібно зробити, - це компілювати з -pgопцією, щоб включити gprof. Потім ми запускаємо програму звичайно з параметром CLI розміру, який виробляє розумну тривалість виконання декількох секунд ( 10000):

gcc -pg -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time ./main.out 10000

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

gcc -pg -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 10000

По-перше, timeрозповідає, що час виконання з і без -pgбув однаковим, що чудово: жодного уповільнення! Однак я бачив облікові записи про 2–3-кратне уповільнення роботи складного програмного забезпечення, наприклад, як показано в цьому квитку .

Оскільки ми компілювали -pg, запуск програми створює файл- gmon.outфайл, що містить дані профілювання.

Ми можемо спостерігати цей файл графічно з gprof2dotзапитом на: Чи можливо отримати графічне зображення результатів gprof?

sudo apt install graphviz
python3 -m pip install --user gprof2dot
gprof main.out > main.gprof
gprof2dot < main.gprof | dot -Tsvg -o output.svg

Тут gprofінструмент зчитує gmon.outінформацію про сліди та формує звіт, прочитаний людиною main.gprof, який gprof2dotпотім читає, щоб створити графік.

Джерело для gprof2dot знаходиться за адресою: https://github.com/jrfonseca/gprof2dot

Ми спостерігаємо за -O0ходом наступного :

введіть тут опис зображення

і для -O3пробігу:

введіть тут опис зображення

Вихідний -O0результат значною мірою пояснює себе. Наприклад, це показує, що 3 maybe_slowвиклики та їхні дочірні дзвінки займають 97,56% від загальної тривалості виконання, хоча саме на виконання maybe_slowбез дітей становить 0,00% від загального часу виконання, тобто майже весь час, проведений на цій функції, витрачався на дитячі дзвінки.

TODO: чому mainвідсутній у -O3висновку, навіть якщо я можу бачити його btв GDB? Відсутня функція з виходу GProf. Я думаю, це тому, що gprof також є вибірковою базою на додаток до складеного інструментарію, і -O3 mainце просто занадто швидко і не має зразків.

Я вибираю вихід SVG замість PNG, оскільки SVG можна шукати за допомогою Ctrl + F, а розмір файлу може бути приблизно в 10 разів меншим. Також ширина та висота згенерованого зображення можуть бути гумористичними з десятками тисяч пікселів для складного програмного забезпечення, і GNOME eog3.28.1 виправляється в цьому випадку для PNG, в той час як SVG-файли відкриваються моїм браузером автоматично. gimp 2.8 добре працював, дивіться також:

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

введіть тут опис зображення

Чи можете ви легко знайти найкритичніший стек викликів за допомогою всіх тих крихітних несортових ліній спагетті, які перетинаються один з одним? Можливо, є кращі dotваріанти, я впевнений, але зараз я не хочу йти туди. Нам дійсно потрібен відповідний переглядач для цього, але я його ще не знайшов:

Однак ви можете використовувати кольорову карту, щоб трохи пом'якшити ці проблеми. Наприклад, на попередньому величезному зображенні мені нарешті вдалося знайти критичний шлях ліворуч, коли я зробив геніальну відрахування, що зелений приходить після червоного, за ним нарешті темніший і темніший синій.

Крім того, ми можемо також спостерігати виведення тексту gprofвбудованого інструменту binutils, який ми раніше зберегли на:

cat main.gprof

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

Після того, як ви зрозуміли формат виведення даних, ви можете зменшити багатослівність, щоб показувати лише дані без уроку з -bможливістю:

gprof -b main.out

У нашому прикладі результати були для -O0:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
100.35      3.67     3.67   123003     0.00     0.00  common
  0.00      3.67     0.00        3     0.00     0.03  fast
  0.00      3.67     0.00        3     0.00     1.19  maybe_slow

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.27% of 3.67 seconds

index % time    self  children    called     name
                0.09    0.00    3003/123003      fast [4]
                3.58    0.00  120000/123003      maybe_slow [3]
[1]    100.0    3.67    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    3.67                 main [2]
                0.00    3.58       3/3           maybe_slow [3]
                0.00    0.09       3/3           fast [4]
-----------------------------------------------
                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]
-----------------------------------------------
                0.00    0.09       3/3           main [2]
[4]      2.4    0.00    0.09       3         fast [4]
                0.09    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common                  [4] fast                    [3] maybe_slow

і для -O3:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
100.52      1.84     1.84   123003    14.96    14.96  common

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.54% of 1.84 seconds

index % time    self  children    called     name
                0.04    0.00    3003/123003      fast [3]
                1.79    0.00  120000/123003      maybe_slow [2]
[1]    100.0    1.84    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     97.6    0.00    1.79                 maybe_slow [2]
                1.79    0.00  120000/123003      common [1]
-----------------------------------------------
                                                 <spontaneous>
[3]      2.4    0.00    0.04                 fast [3]
                0.04    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common

Як дуже швидкий підсумок для кожного розділу, наприклад:

                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]

центрів навколо функції, яка залишена відступною ( maybe_flow). [3]ідентифікатор цієї функції. Над функцією знаходяться його викликаючі, а нижче - виклики.

Бо -O3дивіться тут, як у графічному виході, що maybe_slowі fastне має відомого батьківського, що означає документація <spontaneous>.

Я не впевнений, чи є хороший спосіб зробити покрокове профілювання за допомогою gprof: `gprof` час, витрачений на конкретні рядки коду

valgrind callgrind

valgrind запускає програму через віртуальну машину valgrind. Це робить профілювання дуже точним, але також призводить до дуже великого уповільнення програми. Раніше я також згадав про kcachegrind в: Інструменти для отримання графічної графіки виклику функції коду

callgrind - це інструмент valgrind для профілювання коду, а kcachegrind - програма KDE, яка може візуалізувати вихід кешгринда.

Спочатку нам потрібно зняти -pgпрапор, щоб повернутися до звичайної компіляції, інакше запуск насправді не вдається Profiling timer expired, і так, це так часто, що я це зробив, і для нього виникло запитання щодо переповнення стека.

Отже, ми компілюємо і запускаємо як:

sudo apt install kcachegrind valgrind
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time valgrind --tool=callgrind valgrind --dump-instr=yes \
  --collect-jumps=yes ./main.out 10000

Я вмикаю, --dump-instr=yes --collect-jumps=yesтому що це також скидає інформацію, яка дозволяє нам переглянути порядок продуктивності за збірною лінією при порівняно невеликих додаткових накладних витратах.

Повертаючись, timeговорить нам, що на виконання програми було потрібно 29,5 секунд, тому у нас на цьому прикладі було сповільнення приблизно в 15 разів. Зрозуміло, що це уповільнення стане серйозним обмеженням для великих навантажень. На згадуваному тут "прикладі реального програмного забезпечення" я спостерігав уповільнення 80x.

Програма генерує файл даних профілю, названий, callgrind.out.<pid>наприклад, callgrind.out.8554у моєму випадку. Ми переглядаємо цей файл за допомогою:

kcachegrind callgrind.out.8554

який показує графічний інтерфейс, що містить дані, схожі на текстовий вихід gprof:

введіть тут опис зображення

Крім того, якщо ми переходимо на праву нижню вкладку "Графік викликів", ми бачимо графік викликів, який ми можемо експортувати, натиснувши правою кнопкою миші, щоб отримати наступне зображення з необгрунтованою кількістю білої рамки :-)

введіть тут опис зображення

Я думаю, що fastце не відображається на цьому графіку, оскільки kcachegrind повинен спростити візуалізацію, оскільки цей виклик займає занадто мало часу, швидше за все, це буде поведінка, яку ви хочете в реальній програмі. У меню правої кнопки миші встановлено деякі налаштування, щоб визначити, коли потрібно скинути такі вузли, але я не міг змусити його відображати такий короткий дзвінок після швидкої спроби. Якщо я натискаю на fastліве вікно, воно показує графік виклику з fast, так що цей стек був фактично захоплений. Ніхто ще не знайшов способу показати повний графік виклику графіка: Зробити callgrind показати всі виклики функцій у callgraph kcachegrind

TODO на складному програмному забезпеченні C ++, я бачу деякі записи типу <cycle N>, наприклад, <cycle 11>де я очікую назви функцій, що це означає? Я помітив, що є кнопка "Визначення циклу", щоб увімкнути та вимкнути цю функцію, але що це означає?

perf з linux-tools

perfСхоже, використовують виключно механізми відбору ядра Linux. Це робить його дуже простим у налаштуванні, але також не повністю точним.

sudo apt install linux-tools
time perf record -g ./main.out 10000

Це додало 0,2 секунди до виконання, тому ми чудово розуміємо час, але я все ще не бачу особливого інтересу після розширення commonвузла стрілкою правої клавіатури:

Samples: 7K of event 'cycles:uppp', Event count (approx.): 6228527608     
  Children      Self  Command   Shared Object     Symbol                  
-   99.98%    99.88%  main.out  main.out          [.] common              
     common                                                               
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.01%     0.01%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.01%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.01%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.01%     0.00%  main.out  ld-2.27.so        [.] mprotect            
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.01%     0.00%  main.out  ld-2.27.so        [.] _xstat              
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x2f3d4f4944555453  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007fff3cfc57ac  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

Тоді я намагаюся орієнтувати -O0програму, щоб побачити, чи це щось показує, і лише зараз, нарешті, я бачу графік викликів:

Samples: 15K of event 'cycles:uppp', Event count (approx.): 12438962281   
  Children      Self  Command   Shared Object     Symbol                  
+   99.99%     0.00%  main.out  [unknown]         [.] 0x04be258d4c544155  
+   99.99%     0.00%  main.out  libc-2.27.so      [.] __libc_start_main   
-   99.99%     0.00%  main.out  main.out          [.] main                
   - main                                                                 
      - 97.54% maybe_slow                                                 
           common                                                         
      - 2.45% fast                                                        
           common                                                         
+   99.96%    99.85%  main.out  main.out          [.] common              
+   97.54%     0.03%  main.out  main.out          [.] maybe_slow          
+    2.45%     0.00%  main.out  main.out          [.] fast                
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.00%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.00%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_lookup_symbol_x 
     0.00%     0.00%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.00%     0.00%  main.out  ld-2.27.so        [.] mmap64              
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x552e53555f6e653d  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007ffe1cf20fdb  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

TODO: що сталося на -O3страті? Це просто так maybe_slowі fastбули занадто швидкими і не отримали жодних зразків? Чи добре це працює з -O3більш великими програмами, на виконання яких потрібно більше часу? Я пропустив якийсь варіант CLI? Я дізнався про те, -Fщоб контролювати частоту вибірки в Герц, але я встановив її до максимальної, дозволеної за замовчуванням -F 39500(може бути збільшена за sudo), і я все ще не бачу чітких дзвінків.

perfХороша річ - це інструмент FlameGraph від Брендана Грегга, який дуже акуратно відображає таймінги стеків викликів, що дозволяє швидко бачити великі дзвінки. Інструмент доступний по адресою: https://github.com/brendangregg/FlameGraph і також згадується в його перфорації підручника по адресою: http://www.brendangregg.com/perf.html#FlameGraphs Коли я біг , perfНЕ sudoя ERROR: No stack counts foundтак для тепер я буду це робити з sudo:

git clone https://github.com/brendangregg/FlameGraph
sudo perf record -F 99 -g -o perf_with_stack.data ./main.out 10000
sudo perf script -i perf_with_stack.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

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

введіть тут опис зображення

На більш складному прикладі стає зрозуміло, що означає графік:

введіть тут опис зображення

TODO [unknown]в цьому прикладі є журнал функцій, чому це так?

Інший інтерфейс інтерфейсу Perf, який, можливо, варто його включати:

  • Плагін Eclipse Trace Compass: https://www.eclipse.org/tracecompass/

    Але це є і недоліком того, що вам потрібно спершу перетворити дані у загальний формат сліду, що можна зробити за допомогою perf data --to-ctf, але це потрібно ввімкнути під час створення / мати perfдостатньо новий, будь-який з цих випадків не стосується перф. Ubuntu 18.04

  • https://github.com/KDAB/hotspot

    Мінусом цього є те, що, здається, немає пакету Ubuntu, і для його створення потрібен Qt 5.10, тоді як Ubuntu 18.04 знаходиться на рівні Qt 5,9.

gperftools

Раніше називався "Інструменти ефективності Google", джерело: https://github.com/gperftools/gperftools На основі зразків.

Спочатку встановіть gperftools за допомогою:

sudo apt install google-perftools

Тоді ми можемо включити процесор gperftools CPU двома способами: під час виконання або під час збирання.

Під час виконання ми повинні передати встановлену LD_PRELOADточку libprofiler.so, яку ви можете знайти locate libprofiler.so, наприклад, у моїй системі:

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libprofiler.so \
  CPUPROFILE=prof.out ./main.out 10000

Крім того, ми можемо створити бібліотеку в час зв’язку, розподіляючи проходження LD_PRELOADпід час виконання:

gcc -Wl,--no-as-needed,-lprofiler,--as-needed -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
CPUPROFILE=prof.out ./main.out 10000

Дивіться також: gperftools - файл профілю не скидається

Найприємніший спосіб перегляду цих даних, які я знайшов до цього часу, - це зробити вихід pprof у тому самому форматі, який kcachegrind приймає як вхідний (так, Valgrind-project-viewer-tool) і використовувати kcachegrind для перегляду того:

google-pprof --callgrind main.out prof.out  > callgrind.out
kcachegrind callgrind.out

Після запуску будь-якого з цих методів, ми отримуємо prof.outфайл даних профілю як вихід. Ми можемо розглядати цей файл графічно як SVG за допомогою:

google-pprof --web main.out prof.out

введіть тут опис зображення

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

Крім того, ми також можемо отримати деякі текстові дані за допомогою:

google-pprof --text main.out prof.out

що дає:

Using local file main.out.
Using local file prof.out.
Total: 187 samples
     187 100.0% 100.0%      187 100.0% common
       0   0.0% 100.0%      187 100.0% __libc_start_main
       0   0.0% 100.0%      187 100.0% _start
       0   0.0% 100.0%        4   2.1% fast
       0   0.0% 100.0%      187 100.0% main
       0   0.0% 100.0%      183  97.9% maybe_slow

Дивіться також: Як користуватися інструментами для парфуму Google

Тестовано в Ubuntu 18.04, gprof2dot 2019.11.30, valgrind 3.13.0, perf 4.15.18, ядро ​​Linux 4.15.0, FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b, gperftools 2.5-2.


2
За замовчуванням запис perf використовує регістр покажчика кадру. Сучасні компілятори не записують адресу кадру і замість цього використовують регістр як загальну мету. Альтернативою є компілювати з -fno-omit-frame-pointerпрапором або використовувати іншу альтернативу: записувати з --call-graph "dwarf"або --call-graph "lbr"залежно від сценарію.
Хорхе Беллон

5

Для однопотокових програм можна використовувати igprof , Ignominous Profiler: https://igprof.org/ .

Це пробірник вибірки, відповідно до ... довгої ... відповіді Майка Данлаве, який подарує результати, щоб перетворити результати в дерево стека викликів, яке можна переглянути, з анотацією часу та пам'яті, витраченої на кожну функцію, або сукупного або за функцією.


Це виглядає цікаво, але не вдається компілювати з GCC 9.2. (Debian / Sid) Я зробив випуск на github.
Базиль Старинкевич

5

Також варто згадати

  1. HPCToolkit ( http://hpctoolkit.org/ ) - з відкритим кодом, працює для паралельних програм та має графічний інтерфейс, за допомогою якого можна переглянути результати кількома способами
  2. Intel VTune ( https://software.intel.com/en-us/vtune ) - Якщо у вас є компілятори Intel, це дуже добре
  3. TAU ( http://www.cs.uoregon.edu/research/tau/home.php )

Я використовував HPCToolkit і VTune, і вони дуже ефективні при пошуку довгого полюса в наметі і не потребують перекомпіляції вашого коду (за винятком того, що для отримання значущого результату ви повинні використовувати тип -g -O або RelWithDebInfo типу CMake) . Я чув, що TAU схожий за своїми можливостями.


4

Це два способи, які я використовую для прискорення свого коду:

Для програм, пов'язаних з процесором:

  1. Використовуйте профайлер в режимі DEBUG, щоб визначити сумнівні частини вашого коду
  2. Потім перейдіть у режим ЗВІТКУ та прокоментуйте сумнівні розділи свого коду (заглушіть його нічим), поки не побачите зміни в продуктивності.

Для приєднаних до вводу / виводу програм:

  1. Використовуйте профайлер у режимі ЗВ'ЯЗКУ, щоб визначити сумнівні частини вашого коду.

NB

Якщо у вас немає профілера, використовуйте профілера бідолахи. Натисніть паузу під час налагодження програми. Більшість наборів розробників перервуться на збори з коментованими номерами рядків. Ви статистично схожі на те, що ви потрапите в регіон, який харчується більшістю циклів процесора.

Для процесора причина профілювання в режимі DEBUG полягає в тому, що якщо ваш спробувавший профілювання в режимі RELEASE , компілятор буде зменшувати математику, векторизацію циклів та вбудованих функцій, що, як правило, перетворює ваш код у безмісний безлад, коли він збирається. Нерозбірливий безлад означає, що ваш профілер не зможе чітко визначити, що займає так довго, оскільки збірка може не відповідати вихідному коду під час оптимізації . Якщо вам потрібна продуктивність (наприклад, чутливий до часу) режиму RELEASE , відключіть функції налагодження в міру необхідності, щоб зберегти корисну продуктивність.

Для пов'язаних з входом / виводом профайлер все ще може ідентифікувати операції вводу / виводу в режимі ЗВІТКИ, оскільки операції вводу / виводу або зовні пов'язані із спільною бібліотекою (більшу частину часу), або, в гіршому випадку, призводять до системного, вектор переривання виклику (який також легко ідентифікується профілером).


2
+1 Метод бідолахи працює так само добре, як для пов'язаних вводу-виводу, як і для пов'язаних з процесором, і я рекомендую робити все налаштування продуктивності в режимі DEBUG. Закінчивши налаштування, увімкніть RELEASE. Це поліпшить ситуацію, якщо програма обмежена процесором у вашому коді. Ось грубе, але коротке відео про процес.
Майк Данлаве

3
Я б не використовував DEBUG-збірки для профілювання продуктивності. Часто я бачив, що критичні показники продуктивності в режимі DEBUG повністю оптимізовані в режимі випуску. Іншою проблемою є використання тверджень у коді налагодження, які додають шуму продуктивності.
gast128

3
Ви взагалі читали мій пост? "Якщо вам потрібна продуктивність (наприклад, чутливий до часу) режиму RELEASE, відключіть функції налагодження у міру необхідності для збереження корисної продуктивності", "Потім перейдіть у режим RELEASE і прокоментуйте сумнівні розділи вашого коду (заглушіть його нічим), поки не побачите зміни в продуктивності. " Я сказав, що перевірте можливі проблемні області в режимі налагодження і перевірте ці проблеми в режимі випуску, щоб уникнути згаданого вами клопки.
seo

2

Ви можете використовувати бібліотеку iprof:

https://gitlab.com/Neurochrom/iprof

https://github.com/Neurochrom/iprof

Це кросплатформна платформа і дозволяє не вимірювати продуктивність вашої програми також у режимі реального часу. Можна навіть з’єднати його з живим графіком. Повна відмова від відповідальності: Я - автор.


2

Ви можете використовувати каркас ведення журналу, наприклад, loguruоскільки він включає часові позначки та загальний час роботи, які можна добре використовувати для профілювання:


1

На роботі у нас є дійсно приємний інструмент, який допомагає нам контролювати те, що ми хочемо, з точки зору планування. Це було корисно не раз.

Він знаходиться в C ++ і повинен бути налаштований під ваші потреби. На жаль, я не можу ділитися кодом, лише поняттями. Ви використовуєте "великий" volatileбуфер, що містить часові позначки та ідентифікатор події, який ви можете скинути після смерти або після зупинки системи реєстрації журналу (і скинути це у файл, наприклад).

Ви отримуєте так званий великий буфер з усіма даними, а невеликий інтерфейс аналізує їх і показує події з ім'ям (вгору / вниз + значення), як це робить осцилограф з кольорами (налаштований у .hppфайлі).

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

Вам потрібно 3 файли:

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

Концепція полягає у визначенні подій tool_events_id.hppтаким чином:

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

Ви також визначаєте кілька функцій у toolname.hpp:

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

Де б ви не могли використовувати код:

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

probeФункція використовує кілька складальних ліній , щоб отримати тактову мітку часу ASAP , а потім встановлює запис в буфер. У нас також є атомний приріст, щоб безпечно знаходити індекс, де зберігати журнал події. Звичайно буфер круговий.

Сподіваюся, ідея не затьмарена відсутністю зразкового коду.


1

Насправді трохи здивовано, що багато хто не згадував про google / бенчмарк , в той час, як чітко визначити певну область коду, особливо якщо база коду трохи більша, однак я вважаю це справді корисним при використанні в поєднанні зcallgrind

ІМХО, що визначає частину, яка спричиняє вузьке місце, є ключовим тут. Однак я б спробував відповісти на наступні питання спочатку і вибрав інструмент, заснований на цьому

  1. чи правильний мій алгоритм?
  2. чи є замки, які виявляються шийками для пляшок?
  3. чи є конкретний розділ коду, який виявляється винуватцем?
  4. як щодо IO, оброблений та оптимізований?

valgrindз комбінацією callrindта kcachegrindмає дати гідну оцінку вищезазначеним питанням, і як тільки буде встановлено, що є проблеми з деяким розділом коду, я б запропонував зробити позначку мікро стенда google benchmark- це гарне місце для початку.


1

Використовуйте -pgпрапор під час компіляції та зв’язування коду та запустіть виконуваний файл. Поки ця програма виконується, дані профілювання збираються у файл a.out.
Існує два різних типи профілювання

1- Плоське профілювання:
запустивши команду, gprog --flat-profile a.outви отримали такі дані
- який відсоток від загального часу, витраченого на функцію,
- скільки секунд було витрачено на функцію - включаючи та виключаючи виклики до підфункцій,
- кількість дзвінки,
- середній час на виклик.

2- графік профілює
нам команду gprof --graph a.outотримати наступні дані для кожної функції, що включає
- У кожному розділі одна функція позначена номером індексу.
- Над функцією є список функцій, які викликають функцію.
- Нижче функції, є список функцій, які викликаються функцією.

Щоб отримати більше інформації, ви можете подивитися на https://sourceware.org/binutils/docs-2.32/gprof/


0

Оскільки ніхто не згадав про MAP Arm, я додав би це, як особисто я успішно використовував Map для профілю наукової програми на C ++.

Arm MAP - це профайлер для паралельних, багатопотокових або однопотокових кодів C, C ++, Fortran і F90. Він забезпечує глибокий аналіз та чітке вказівка ​​вузького місця до вихідної лінії. На відміну від більшості профілів, він розроблений так, щоб мати змогу проводити профіль pthreads, OpenMP або MPI для паралельного та потокового коду.

MAP - комерційне програмне забезпечення.


0

використовувати програмне забезпечення для налагодження, як визначити, де код працює повільно?

просто подумайте, що у вас є перешкода, поки ви перебуваєте в русі, це знизить вашу швидкість

наприклад, що циклічне небажане перерозподіл, переповнення буфера, пошук, витоки пам'яті тощо. Операції вимагають більшої потужності виконання, це негативно вплине на виконання коду. Обов'язково додайте -pg до компіляції перед профілюванням:

g++ your_prg.cpp -pgабо cc my_program.cpp -g -pgвідповідно до вашого компілятора

ще не пробував цього, але чув хороші речі про google-perftools. Це, безумовно, варто спробувати.

valgrind --tool=callgrind ./(Your binary)

Він створить файл під назвою gmon.out або callgrind.out.x. Потім ви можете скористатися інструментом kcachegrind або налагоджувачем для читання цього файлу. Це дасть вам графічний аналіз речей з результатами, на зразок яких рядків коштує скільки.

я думаю так

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