Обновить

Сколько на самом деле стоит LOG_INFO(): benchmark библиотек логирования C++

Уровень сложностиСредний
Время на прочтение6 мин
Охват и читатели7.3K
Всего голосов 7: ↑7 и ↓0+9
Комментарии9

Комментарии 9

спасибо, а почему вы для бенчмаркинга выбрали именно эти библиотеки? они по-вашей оценке наиболее развитые?

spdlog, пожалуй, действительно самая известная библиотека. Что касается quill и easylogging++, их предложил ChatGPT в ответ на мой вопрос о том, какие ещё решения наиболее распространены в мире логирования.

logme — это, собственно, моя библиотека. Изначально я ставил себе задачу сравнить реальную производительность различных библиотек. Поскольку результаты получились достаточно интересными, решил ими поделиться.

Хотелось бы уточнить. Эти библиотеки mt-safe?

Насколько дорого гарантировать потоко-безопасность?

В бенчмарке большинство библиотек уже работали в MT-safe режиме (включен в режиме “из коробки”), тогда как easylogging++ согласно документации — нет. Поэтому сравнение не занижает её результат — скорее наоборот.

Лет 10 назад доводилось реализовывать логирование для высоко нагруженных систем.
Идея была − минимум времени/работы в рабочем потоке. На языке вашего теста все бы свелось к проверке уровня логирования. Если null, то на этом бы расходы и закончились. В противном случае динамические параметры для форматирования, А также временной штамп, ID потока, И уникальный ID сообщения (раздается независимо от потока), закидывались в потокобезопасную очередь. А потом уже другой поток, ответственный за логирование это все разгребал, используя вспомогательные потоки.

Мне кажется у Вас в статье ошибочное допущение, что разработчики высоконагруженных приложений вот так вот возьмут и будут логировать "тяп−ляп из коробки". Это верно только частично - eсли производительность приложения их устраивает. В портивном случае, они задумаются, как оптимизировать логирование и ваш бенчмарк потеряет актуальность.

Спасибо, вы описываете абсолютно здравый и, по сути, канонический подход для high-load систем — минимальная работа в рабочем потоке и перенос всего остального в асинхронную обработку.

И да, вы правы: в реальных проектах логирование почти никогда не используется «из коробки». Его настраивают под конкретные требования — формат, состав полей (timestamp, thread id и т.д.), синхронный/асинхронный режим, буферизацию, фильтры и прочее. В этом смысле итоговая производительность определяется не только библиотекой, но и архитектурой логирования в целом.

В бенчмарке же была немного другая цель. Мы сознательно упростили сценарий (логирование только сообщения без дополнительных полей), чтобы привести разные библиотеки к максимально сопоставимым условиям. Это не попытка смоделировать production, а скорее попытка получить «базовую стоимость» вызова логирования при прочих равных.

Если сразу сравнивать «как в жизни», то получится сравнение конкретных конфигураций (и даже конкретных подходов к логированию), а не библиотек — и такой результат будет сильно зависеть от выбранных настроек.

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

А дальше — полностью согласен — в реальной high-load системе без дополнительной настройки логирования обычно никуда

Некоторое время назад сделал свой логгер - просто потому что захотелось упороться в производительность. С берчмарками, все дела. Так вот, что меня удивило - очень большая разница cout / fwrite и ofstream / fopen / mapping подходов на MSVC. В то время как на gcc / cmake все эти методы дают практически одинаковую производительность, в MSVC разница в первом случае 18 : 1, во втором - 9 : 1.22 : 1
Кому интересно, поковыряться можно тут.

Null-сценарий — если в библиотеке корректно реализовано отключение вывода через уровень (например, выставлен LOG_WARN в конфигурации), то вызов LOG_INFO обходится в 1-2 нс (~5–7 тактов), практически ничего: ранний выход без подготовки аргументов и форматирования. В fmtlog, например, это реализовано именно так.

В бенчмарке в горячем пути на каждую итерацию вызывается Clock::now(), что добавляет постоянный overhead ~3–10 нс и занижает измерения для всех библиотек, особенно заметно в null сценарии. Разница для logme(c) в null cценарии x1.7.

Довольно интересная библиотека: каналы с линковкой, автолог входа/выхода из функций, compile-time обфускация, one-time сообщения per-object, crash логи. Интересно было бы посмотреть на бенчмарк библиотеки если вместо std::format использовать библиотеку {fmt}.

Спасибо за комментарий.

В logme в null-сценарии происходит ранний выход до любой дополнительной логики, включая получение времени — Clock::now() в этом пути не вызывается.

Поэтому для logme указанный overhead на результат не влияет.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации