Как я могу профилировать C++ код, работающий в Linux?

У меня есть приложение на С++, работающее на Linux, которое я в процессе оптимизации. Как я могу определить, какие области моего кода работают медленно?

Ответ 1

Если ваша цель - использовать профилировщик, воспользуйтесь одним из предложенных.

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

Просто остановите его несколько раз, и каждый раз смотрите на стек вызовов. Если есть какой-то код, который тратит впустую некоторый процент времени, 20% или 50% или что-то еще, то есть вероятность, что вы поймаете его в действии на каждом образце. Так что это примерно процент образцов, на которых вы это увидите. Там не требуется образованное предположение. Если у вас есть предположение относительно проблемы, это докажет или опровергнет ее.

У вас может быть несколько проблем с производительностью разных размеров. Если вы уберете какой-либо из них, остальные будут занимать больший процент, и его будет легче обнаружить при последующих проходах. Этот эффект увеличения в сочетании с несколькими проблемами может привести к действительно огромным факторам ускорения.

Предостережение: программисты склонны скептически относиться к этой технике, если они сами ее не использовали. Они скажут, что профилировщики предоставляют вам эту информацию, но это верно только в том случае, если они производят выборку всего стека вызовов, а затем позволяют исследовать случайный набор выборок. (Сводные данные - то, где понимание потеряно.) Графики вызовов не дают вам ту же информацию, потому что

  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) составляет 0,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 раза, и она выпадает в голову оба раза, что это говорит вам о вероятном весе монеты? Уважаемый способ ответа - сказать, что это бета-распределение со средним значением (количество попаданий + 1)/(количество попыток + 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%.

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

enter image description here

Измерение горизонтальное; он говорит вам, какую часть времени занимают определенные подпрограммы. Выборка вертикальная. Если есть какой-либо способ избежать того, что в данный момент делает вся программа, и если вы видите это во втором примере, вы нашли узкое место. Это то, что имеет значение - видеть всю причину потраченного времени, а не только сколько.

Ответ 2

Вы можете использовать Valgrind со следующими параметрами

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

Он будет генерировать файл с именем callgrind.out.x. Затем вы можете использовать инструмент kcachegrind для чтения этого файла. Это даст вам графический анализ вещей с результатами, например, какие из них стоят.

Ответ 3

Я предполагаю, что вы используете GCC. Стандартным решением будет профиль с gprof.

Обязательно добавьте -pg в компиляцию перед профилированием:

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

Я еще не пробовал, но я хорошо слышал о google-perftools. Это определенно стоит попробовать.

Связанный с этим вопрос здесь.

Несколько других слов, если gprof не выполняет эту работу для вас: Valgrind, Intel VTune, Sun DTrace.

Ответ 4

Новые ядра (например, последние ядра Ubuntu) поставляются с новыми инструментами "perf" (apt-get install linux-tools) AKA perf_events.

Они поставляются с классическими пробоотборниками пробоотбора (man-страница), а также удивительным timechart!

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

Alt text

Ответ 5

OProfile хорошо, потому что это делает его намного легче, чем Gprof, чтобы просмотреть несколько программ одновременно. Вы также можете запустить его в своей сборке релизов (если у него есть символы), вместо того, чтобы создавать специальную сборку профилирования.

Если вы не заботитесь о массивном ударе производительности (50x), Valgrind (Cachegrind) хорошо.

Ответ 6

Вы можете использовать callgrind. Вместе с KCacheGrind он дает довольно хороший профилировщик. Кроме того, Intel VTune является бесплатным для образовательного использования в Linux. Вероятно, это лучший профайлер. Если у вас есть процессор AMD, используйте AMD Codeanalyst (сменил AMDs CodeXL), который также доступен для Linux; этот только приличный, но свободный.

Ответ 7

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

(wikipedia) Valgrind по сути является виртуальным машина, использующая точно в срок (JIT) методы компиляции, в том числе динамическая перекомпиляция. Ничего от исходная программа когда-либо запускается непосредственно на главном процессоре. Вместо этого Valgrind сначала переводит программа во временную, более простую форму промежуточное представление (IR), который является нейтральным от процессора, Форма на основе SSA. После преобразования, инструмент (см. ниже) можно свободно делать какие бы преобразования он ни хотел на IR, прежде чем Valgrind переводит IR обратно в машинный код и позволяет хост-процессор запускает его.

Callgrind - это профайлер, основанный на этом. Главное преимущество заключается в том, что вам не нужно запускать приложение в течение нескольких часов, чтобы получить надежный результат. Даже один второй прогон достаточен для получения надежных и надежных результатов, потому что Callgrind является не-зондирующим профилировщиком.

Еще один инструмент, основанный на Вальгринде, - это массив. Я использую его для использования памяти памяти кучи. Он отлично работает. Что он делает, так это то, что он дает вам снимки использования памяти - подробная информация WHAT содержит WHAT процент памяти, и ВОЗ положила его туда. Такая информация доступна в разные моменты времени запуска приложения.

Ответ 8

Ответ на запуск 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.*

В следующем окне я рекомендую щелкнуть заголовок столбца "Self", иначе он показывает, что "main()" - это самая трудоемкая задача. "Я" показывает, сколько каждой функции само по себе занимает время, а не вместе с иждивенцами.

Ответ 9

Это ответ Назгоба Гпрофа на ответ.

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

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

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

Ответ 10

Oprofile - достойный бесплатный вариант, но мне повезло с помощью Zoom. Это коммерческий (бесплатный eval) профилировщик для Linux, который имеет графический интерфейс sweeet для просмотра горячих точек в исходном коде.

Ответ 11

google-perftools является единственной разумной альтернативой Gprof Я нашел. Он довольно полезный, знакомый, и я считаю, что это время выборки, так что узкие места ввода-вывода выявлены, в дополнение к обычным узким местам, которые Gprof обнаруживает. Это также значительно менее инвазивное.

Ответ 12

Используйте Valgrind

Я уверен, что вы можете использовать Cachegrind или какой-либо подобный плагин для профилирования.

Ответ 13

Вы можете посмотреть Gprof, профилировщик GNU.

Другим интересным инструментом может быть IBM Rational Quantify, но он не бесплатный.

Ответ 14

В дополнение к Intel Vtune/AMD CodeAnalyst, perfmon2 - альтернатива OSS, для которой требуется исправленное ядро, чтобы открыть CPU счетчик производительности, и это даст вам различные показатели производительности, которые вы можете собрать.

perfmon2 по-прежнему специфичен для реализации, т.е. L2 промахи кэша называются по-разному на Intel Pentium III по сравнению с AMD64, и они начинают работу над perfmon3, что должно унифицировать API.

Но обычно Gprof будет работать достаточно хорошо, чтобы вы могли обнаружить медленный код.

Ответ 15

Вы можете использовать сборщик/анализатор Sun Studio. Используя сбор может также использовать использование памяти памяти, потоки, MPI и т.д. Вы также получите хорошее представление временной шкалы вашей программы.

Если вы используете эти инструменты в Solaris, вы также можете получить аппаратное обеспечение информацию счетчика производительности, например, в режиме vtune или oprofile.

Вы можете получить этот (и другие очень полезные инструменты от Sun) в Oracle Solaris Studio 12.3.

Ответ 16

Мне очень повезло с Rational Quantify (дорого, но очень хорошо) и OProfile. Помните, что при использовании OProfile он является статистическим профилировщиком, а не профилировщиком полного профиля, например Quantify. OProfile использует модуль ядра для того, чтобы вытолкнуть в стек вызовов каждый запущенный процесс на каждом интервале, поэтому определенные поведения не могут быть пойманы. Использование нескольких профилографов является хорошим, особенно потому, что различные профилировщики, как правило, дают вам разные данные, которые полезны.

Что касается использования Gprof, это нормально. Я бы получил один из графических интерфейсов, так как данные могут быть довольно трудными для прохождения только в командной строке. Я бы избегал Valgrind, пока вам не понадобится проверка памяти.

Ответ 17

Используйте Valgrind, callgrind и kcachegrind:

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

генерирует callgrind.out.x. Прочитайте это, используя kcachegrind.

Используйте gprof (добавьте -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)

Ответ 18

Не забывайте плохой профайлер, который в основном является оберткой для GDB, поэтому он может делать выборку, где потоки "есть" каждый так часто. В основном он использует GDB для получения стеков для выборки вместо lsstack и т.д.

Немного несвязанный, но Gprof сам может также выполнить профилирование выборки: Использовать callgrind как профилировщик выборки? ( "Плоский профиль основан в основном на выборке" )

И вы также можете использовать сигнал USR1 и др. для обратных трассировок нисходящих потоков.

Ответ 19

Существует также LTTng. Я никогда не использовал этот, хотя я не могу сказать, насколько он работает. Но одно преимущество - это трассировка пользовательского пространства. В некоторых ситуациях, которые могут быть довольно приятными.

Ответ 21

Профайлер PCT использует подход выборки в стеке, рекомендованный другими респондентами, которые не предлагали конкретного инструмента.

Он может выполнять профилирование на уровне инструкций или на уровне процедур. Я использовал его для профилирования кода, отличного от C, в дополнение к коду C (я использовал его ранее для OCaml с интересными результатами).

Ответ 22

Используйте SysProf; Это довольно хорошо, делая быстрое профилирование с минимальными хлопотами. Он показывает информацию о том, как много раз вызывалась функция, время, затрачиваемое на каждую процедуру, общее время, затрачиваемое на каждую функцию, и т.д. Просто установите, выполните начальную конфигурацию, а затем запустите приложение/службу (нет необходимости перекомпилировать instrument ваш код).

Ответ 23

Для однопоточных программ вы можете использовать igprof, Ignominous Profiler: https://igprof.org/.

Это профилировщик выборки, аналогичный ответу... long... Mike Dunlavey, который будет обернуть результаты в дерево стека вызовов с возможностью просмотра, снабженное комментариями с указанием времени или памяти, потраченных на каждую функцию, либо совокупную, либо за функции.

Ответ 24

Вот два метода, которые я использую для ускорения моего кода:

Для приложений с привязкой к ЦП:

  • Используйте профилировщик в режиме DEBUG для определения сомнительных частей вашего кода.
  • Затем переключитесь в режим RELEASE и закомментируйте сомнительные разделы вашего кода (не забудьте его ничем), пока не увидите изменения в производительности.

Для связанных приложений ввода/вывода:

  • Используйте профилировщик в режиме RELEASE для определения сомнительных частей вашего кода.

N.B.

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

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

Для I/O-привязки профилировщик все еще может идентифицировать операции ввода-вывода в режиме RELEASE, поскольку операции ввода-вывода либо внешне связаны с общей библиотекой (большую часть времени), либо в в худшем случае, приведет к вектору прерывания sys-call (который также легко идентифицируется профилировщиком).

Ответ 25

Вы можете попробовать профайлер AMD CodeXL. Он бесплатный и доступен для Linux.

Профайлер AMD CodeXL CPU заменяет более не поддерживаемый инструмент CodeAnalyst (который упоминался в ответе выше, указанном Anteru).

CodeXL поддерживает несколько методов профилирования CPU для процессоров AMD (для процессоров, отличных от AMD) поддерживается только профилирование профилей на основе времени.

Дополнительную информацию и ссылки для загрузки можно найти на странице веб-страницу AMD CodeXL.

Ответ 26

Также стоит упомянуть

  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 схожи по возможностям.

Ответ 27

Вы можете использовать библиотеку iprof:

https://gitlab.com/Neurochrom/iprof

https://github.com/Neurochrom/iprof

Он кроссплатформенный и позволяет не измерять производительность вашего приложения также в режиме реального времени. Вы даже можете связать это с живым графиком. Полный отказ от ответственности: я автор.

Ответ 28

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

Ответ 29

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

Это в 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 использует несколько сборочных строк для извлечения метки времени часов как можно скорее, а затем устанавливает запись в буфере. У нас также есть атомарный инкремент для безопасного поиска индекса, в котором будет храниться событие журнала. Конечно, буфер является круглым.

Надеюсь, что идея не запутана отсутствием примера кода.

Ответ 30

Поскольку никто не упомянул Arm MAP, я бы добавил, что лично я успешно использовал Map для профилирования научной программы C++.

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

MAP - коммерческое программное обеспечение.