Новый интрузивный профайлер для GNU Linux и других платформ

It works fast with huge data. 17Mb/s parsing speed and it was tested with 2Gigs of data (requires about 15Gb of RAM)
It works fast with huge data. 17Mb/s parsing speed, and it was tested with about 500Megs of data.

В Linux имеется достаточно большое количество средств для профилирования приложений, наиболее распространенными из них являют valgrind, gprof и oprofile. В valgrind имеется инструмент callgrind, который позволяет собирать статистику по вызовам различных функций. Для анализа результатов есть замечательное приложение — kcachegrind, позволяющее достаточно удобно просматривать узкие места, находить пути, по которым код приходит в эти места.

Это замечательный утилиты, которые позволяют найти узкие места в коде, однако у использования данных утилит есть несколько минусов:

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

Эти особенности общие для всех утилит профайлинга, не требующих изменения исходного кода приложения. Под Linux есть некоторое количество профайлеров, требующих изменения исходного кода, однако мне не удалось найти функционального GUI приложения для просмотра этих данных. Единственное исключение это Telemetry от RadGame tools, но данная утилита не бесплатна.

Не так давно у меня возникла необходимость проверить как работает мой 3D движок и посмотрев на варианты я решил что нужно сделать свое средство, отвечающие минимальному набору требований:

  • Максимально быстрая генерация профилировочных данных — мне нужно смотреть на достаточно быстрые процессы
  • Максимально простой формат выходных данных
  • Возможность профилирования много-поточных приложений
  • Минимально возможное использование Linux-specific кода

В результате был написан cppprofiler, работающий исключительно с испльзованием функционала c++11.

Внутреннее устройство

cppprofiler активно использует функционал из стандарта C++11:

  • Thread-local storage
  • std::chrono
  • std::thread

На данный момент весь требуемый функционал поддерживает gcc-4.8 и последняя версия clang-3.3. В теории выкинув thread-local можно собрать однопоточную версию используя VS2012, но Microsoft в std::chrono в качестве high_resolution_clock использует крайне не точный таймер.

Внешний интерфейс

Фактически весь интерфейс состоит из двух статических функций:

  • startModule(const char *module_name) — открывает модуль профилирования. Допускается открытие вложенных модулей
  • endModule() — закрывает модуль профилирования

Для упрощения жизни имеются вспомогательные макросы:

  • PROFILE_START(name) — аналогичен вызову startModule
  • PROFILE_END — аналогичен вызову endModule
  • PROFILE_FUNCTION — создает вспомогательный класс ProfileHelper и передает туда имя функции (__FUNCTION__)

Также есть вспомогательный класс, реализующий идиому RAII — ProfileHelper. В конструкторе он вызывает startModule а в деструкторе — endModule

Настойки

Профайлер обладает минимальным размером настроек, реализованных с помощью директив препроцессора:

  • PROFILING_CACHE_SIZE — размер кеша для профилировочных данных. Данные скидываются на диск только по заполнению кеша либо при завершении работы
  • PROFILING_FILENAME_PREFIX — префикс который будет писаться к именам файлов. Имя файла для вывода информации формируется по следующему правилу: $PROFILING_FILENAME_PREFIX_$TIMESTAMP_$THREADID.cppprof
  • ENABLE_PROFILING — включает вывод профилировочных данных с помощью макросов. Если данная директива не установлена профайлер всеравно будет работать при прямых вызовах либо при использовании ProfileHelper

Особенности реализации

Performance of the libcppprofiler it self - about 150ns
Performance of the libcppprofiler it self — about 150ns on i7

Как уже говорилось выше — основное требование к данному профайлеру — скорость работы. Для увеличения скорости работы были применены следующие меры:

  • Вся профилировочная информация сохраняется во временном буфере, сброс которого происходит в одном из трех случаев:
    • Закончилось место в буфере
    • Завершается работа приложения
    • Вызван метод flushProfiling
  • Для сохранения данных используется сплошной массив (char*), в который данные пишутся последовательно. Невозможно извлечь записанные ранее данные.
  • Расчеты внутри профайлера сведены к минимуму, поэтому в данные записываются timestamp в наносекундах
  • Для определения времени, потраченного на внутренние нужды профайлера, выводятся внутренние времена на выполнение все операций
  • Профайлер использует thread-local storage чтобы избежать использование примитивов синхронизации. В TLS хранится инстанс профайлера для треда и время старта треда

Использование std::chrono достаточно спорное решение в случае с профайлером. С одной стороны это в теории кросс-платформенное решение позволяющее добиться высокой точности в измерениях времени, но с другой стороны — стоимость получения времени средствами std::chrono несколько выше использования платформо-специфичных средств. (например использование clock_gettime под linux позволяет уменьшить время работы профайлера примерно на 30%)

Точность std::chrono высока на большинстве платформ, так на gcc-4.8 она достигает 1 нс на Intel Core I7 и в районе 10-50 нс на различных процессорах AMD. Даже в среде VirtualBox точность не превышает 1мкс. Но под VS2013 она может составлять 500мкс, что уже не приемлемо.

Где взять это чудо?

Это чудо можно взять здесь:

  • GitWeb
  • GitHub
  • Или скачать с помощью Git: git clone git://developer.nsws.ru/libcppprofiler.git или git clone https://github.com/Kvalme/libcppprofiler.git

Для сборки потребуется:

  • gcc-4.8 (или можно вручную настроить сборку с помощью clang-3.3)
  • QT-5.0 для сборки UI

Бинарную версию можно взять тут: libcppprofiler.tar.bz2 (260Kb)

Тестовые данные для нее можно либо сгенерировать с помощью perftest либо с скачать: TestData (18Mb)

Ну и на последок несколько скриншотов, чтобы можно было лучше оценить возможности приложения:

 


Comments are closed.