Выбор инструмента
![image](https://habrastorage.org/getpro/habr/post_images/d41/865/230/d41865230077b3cf477f18c15253caea.png)
Проблема профилировки рано или поздно встает перед любым проектом, претендующим на роль лучшего в своей области. Какой инструмент выбрать — всегда большой вопрос. Одни инструменты показывают одну часть картины, другие другую. И рано или поздно начинаешь писать свой тул (англ. tool — орудие\инструмент), который отвечает на насущные проблемы именно данного конкретного проекта. Однако время на написание своего «орудия» всегда приходится вычитать из времени отведенного на сам проект.
Поэтому серьезный профайлер написать не получается…
Но как получить все и сразу? (Тут мне почему то вспоминается песня Queen «I want it all»)
А что если был бы такой API, который позволяет воспользоваться силой уже существующих профайлеров и расширяет их область применения на наши нужды?
Так вот, такой API есть. И, более того, он бесплатный и с открытым исходным кодом. Называется Intel® Single Event API, сокращенно Intel® SEAPI.
Давайте посмотрим, что это такое и насколько сложно им пользоваться.
Что он нам дает?
Судя по wiki официального сайта, этот API работает на Windows, Linux, MAC OS X, Yocto и Android. И позволяет визуализировать свои трассы в Systrace, chrome://tracing, Windows Performance Analyzer, XCode Instruments, QT Creator profiler, Trace Compass. Так же он умеет конветировать трассы в два способа отображения диаграмм: DGML и GraphViz. Выберите из этого то, что Вам наиболее знакомо и вперед!
Как этим пользоваться?
Сначала его придется скачать и скомпилировать. Это не сложно, если у вас уже есть cmake, python и «build environment» (компиляционное окружение). На Windows это студия 2013, ниже нельзя, потому что там уже во всю используется С++11. Есть и поддержка 2015 ой студии. На других платформах каждый сам знает все стандартные средства для сборки.
Сборка очень проста: в корневой директории запустите «python buildall.py -i» это не просто скомпилирует проект, но и соберет инсталлер.
Для ленивых (как я) недавно был выложен готовый инсталлятор: github.com/01org/IntelSEAPI/releases
После инсталляции в папке bin можно найти пример использования «TestIntelSEAPI» — он показывает все возможности, доступные на текущий момент.
Дальше проще — добавляем к себе в проект itt_notify.hpp и расставляем макросы из него в своем коде.
Самый простой и полезный макрос — это ITT_FUNCTION_TASK() — просто оставим его в начале тех функций, которые нас больше всего волнуют. Все время жизни функции будет замеряно каждый раз как она вызывается.
Если вдруг замерять надо нечто меншее чем функция, есть макрос ITT_SCOPE_TASK(name).
К обоим макросам можно добавлять аргументы: ITT_ARG(name, value).
Также можно трассировать и «счетчики» ITT_COUNTER(name, value).
Для линковки понадобится включить в проект «ittnotify*.lib» из папки bin.
Однако, если наш проект написан на чистом «C» — эти макросы не подойдут. Но есть и хорошая новость — эти макросы ни что иное, как обертка над более низкоуровневым трассировочным API — itt_notify.
Примеры использования и того и другого можно найти в InstrumentationExample.cpp
Есть хорошие новости и для любителей (а то и профессионалов) скриптовых языков: совсем недавно была добавлена поддержка Python и Java. Примеры даны прямо в самих обертках.
Запускаем, отбегаем и ...
Вот, что мы получим, например, в Windows Performance Analyzer:
![image](https://habrastorage.org/getpro/habr/post_images/f94/4d2/07a/f944d207a1d1bddf0ada207bfe22a7d8.png)
WPA хорош тем, что он уже многое умеет. Очень многое. Наша задача только добавить в него свои события и счетчики — тогда можно будет воспользоваться всей его мощью на благо своего проекта.
Ту же трассу посмотрим с помощью GraphViz (из трассы получился run-time call-graph):
![image](https://habrastorage.org/getpro/habr/post_images/eed/d11/8e0/eedd118e069005c145d4536dfb18d112.png)
Ну и конечно XCode Instruments:
![image](https://habrastorage.org/getpro/habr/post_images/6a0/e98/9f7/6a0e989f72fcfdb599373cfec761b065.png)
Про него кстати есть отличный пост на хабре: habrahabr.ru/post/168491
— А что насчет памяти?
— Ах да, память!
Профилировка памяти заключается в добавлении memory.cpp к себе в проект. Все CRT аллокации будут представлены в виде счетчиков — на каждый размер аллокации отдельный счетчик. Это позволит видеть общую картину по времени, найти накопления памяти и ее утечки.
Вот, например, как это будет выглядеть в chrome://tracing:
![image](https://habrastorage.org/getpro/habr/post_images/203/8b2/40c/2038b240c245ba1dc6323b5e1c0bc55a.png)
К тому же, есть и атрибутирование операций с памятью на функции. Для понимания как это работает разберем пример. Тестовое приложение, которое поставляется вместе с Intel® SEAPI, запускает потоки, нещадно атакующие itt_notify. Так вот, создание потока, почему-то, в первый раз много медленнее, чем в остальные разы.
Обратите внимание — на картинке видно блоки каких размеров и сколько раз выделялись (освобождались). Это и есть атрибутирование. Например, видно, что в процессе выполнения этой задачи блок размера 8 был удален. Остальные выделялись и в сумме дали 1168 байт.
Атрибутирование основано на простом принципе: все операции с памятью находящиеся в интервале исполнения задачи (на том же потоке) привязываются к этой задаче.
Так почему же в первый раз задача CreateThread была такой медленной?
Ого! Она выделяла память… и много памяти! Целых 154 килобайта. Чтобы понять, на что пошла память, посмотрим самый жирный блок. Блок размером 32776 был выделен аж три раза. И кто это сделал? А тут нам помогут стеки, которые собираются на каждый вызов itt_notify (если их конечно включить).
Находим на треке относящемся к данной аллокации первый из трех «сэмплов» (обведено красным) и смотрим стек.
Вот так неожиданность… обыкновенный захват критической секции приводит к целой свистопляске с бубном — очень похоже на инициализацию при первом обращении. То же показывает исследование и других многочисленных аллокаций — инициализация всего и вся. Это обьясняет длительный первый вызов std::thread, который обведен синим на картинке.
chrome://tracing это уникальная вещь — профайлер, встроенный непосредственно в браузер. Он доступен везде, да и способностей по отображению данных ему не занимать. Эти особенности, на мой взгляд, и делают его основным фаворитом Intel® SEAPI.
![image](https://habrastorage.org/getpro/habr/post_images/73a/bcf/529/73abcf529d7e0cc76cd46f7b28767e3a.png)
Самое приятное в Intel® SEAPI это его легкая расширяемость. Хочешь добавить новый язык — 150 строк кода на новом языке. Хочешь добавить новый формат или вьювер — 100 строк кода на питоне. Хочешь использовать в тестовой инфраструктуре для ночного контроля перформанса — запускай тесты, добавив две переменных окружения и будут трассы. Лениво трассы каждый раз открывать — сконвертируй в свой формат основную статистику и встрой в тестовый репрот.
В общем, на мой взгляд вещь архи-полезная. Надо только потратить какое-то время и с ней разобраться.
Пожалуйста, пишите ваши отзывы, они помогут мне улучшить статью.
Only registered users can participate in poll. Log in, please.
Кто может проверить на других операционках не заявленных в документации?
18.75% Я!3
81.25% Не Я!13
16 users voted. 23 users abstained.
Only registered users can participate in poll. Log in, please.
Кто хочет добавить другие языки программирования?
25% Я добавлю С#2
12.5% Я добавлю JavaScript1
0% Я добавлю Objective-C0
25% Я добавлю Lua2
0% Я добавлю Swift0
0% Я добавлю R0
0% А я Ruby добавлю0
12.5% А Go добавлю я!1
37.5% А я добавлю язык, который тут не указан3
8 users voted. 29 users abstained.
Only registered users can participate in poll. Log in, please.
Кто хочет добавить новый формат\въювер?
21.43% Дык я!3
78.57% А чо я то?11
14 users voted. 22 users abstained.