Pull to refresh

Comments 26

Интересно, как эти библиотеки изменятся с массовым внедрением C++ 26 reflections?

В идеале можно было бы вообще убрать всё ручное расставление tracepoints в коде, заменив на их автоматическую генерацию...

Reflection в C++26 действительно может сильно помочь с boilerplate: автодамп структур, имена полей, enum и т.п.

Но trace points решают немного другую задачу. Тут важно не “автоматически логировать всё”, а осмысленно отметить диагностически важные места с минимальным overhead и возможностью точечного включения в production

reflection не знает, какие состояния, переходы или редкие события действительно важны для расследования проблемы

Не раскрыта тема накладных расходов на “спящие” trace point. Так-то в нагруженных участках кода даже логирование стараются не оставлять (даже не смотря на то, что из этого логирования по факту остается только лишь один if на проверку log-level), а тут подразумевается большое количество trace point-ов в коде. Они же должны оказывать влияние на скорость и тем, что хотя бы один if делается + размер кода растет за счет наличия таких if-ов.

полностью бесплатных trace point-ов в природе не бывает: остаётся небольшой код в месте вызова и несколько очень дешёвых операций. но в этой реализации “спящий” trace point не похож на обычное отключённое логирование. он не строит сообщение, не форматирует данные, не создаёт контекст, не вызывает backend-ы и не захватывает lock-и. после первой регистрации он фактически сводится к relaxed-счётчику срабатываний и проверке флага включения. так что присутствие не абсолютно бесплатное, но близкое к этому

да и сама регистрация это процедуре без выделения памяти и представляет собой встраивание статической структуры с указателем на следующий trace point по статическому же хеду однонаправленного списка. конечно же тут есть захват лока. т.е процедура регистрации наиболее затратна потенциально. но только при конкуренции из других потоков

обычный проход через отключенную trace point сводится по сути только в вызову InterlockedIncrement для счетчика

но в этой реализации “спящий” trace point не похож на обычное отключённое логирование. он не строит сообщение, не форматирует данные, не создаёт контекст, не вызывает backend-ы и не захватывает lock-и

В нормальных системах логирования ничего этого нет, если уровень логирования ниже активного сейчас. Фактически, это один if внутри которого идет одно атомарное чтение этого самого активного уровня. А все, что вы перечислили выполняется только внутри then-а.

в данном случае корректнее сравнивать не с плохим логированием, а с назначением механизма. trace point не пытается быть быстрее обычного disabled logging по самому факту проверки. его отличие в том, что это заранее встроенная диагностическая точка с отдельным runtime control и счётчиком попаданий, которую можно включить точечно без рестарта и без изменения общего уровня логирования. Это по сути и не является вызовом для логирования
опять же нужно понимать, что это есть инструмент. как и все остальные, не стоит его использовать бездумно. вставлять во все самые требовательные к производительности участки кода. можно и ноутбуком в принципе гвозди забивать

Мне кажется, что вы не понимаете суть высказанной мной претензии к вашей статье. Я прекрасно понимаю роль этих ваших trace points. Но ваша статья выглядит откровенно неполной без упоминания того, какие накладные расходы эти самые trace points несут. Типа того, что вот эта процедура вообще без trace points в таком-то бенчмарке показывает время N sec, а с trace points – (N sec + 150ms). Или (N sec + 150 sec).

бенчмарки я думаю это скорее материал для отдельной статьи. в данном случае я видел свою задачу в том чтобы обсудить проблему как таковую. что касается устройства трейс поинтов и почему я считаю их реализацию "почти бесплатными" я привел конкретные разъяснения выше

в данном случае я видел свою задачу в том чтобы обсудить проблему как таковую

Ну вот по крайней мере одному читателю этого оказалось недостаточно. А может и не одному, просто одному стало не лень об этом написать.

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

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

Там дело не только в if-е. А в том, что код с trace point-ами будет выглядеть как куча if-ов с некоторым (подозреваю) не сильно маленьким телом then. Что раздувает код выполняемой функции и это может сказаться общей скорости работы. Поэтому и хотелось бы видеть в статье бенчмарки.

Влияние на размер кода, instruction cache и layout функции возможно, особенно если бездумно вставлять trace point-ы в горячие маленькие функции или внутрь плотных циклов. Trace point-ы не нужно ставить “везде”. Как и обычные логи, они должны стоять в местах, где диагностическая ценность выше потенциальной цены. Да их вообще можно не вставлять если их ценность не кажется приемлемой. Как я уже упомянул выше это просто инструмент. Его можно применять разумно, а можно неразумно. Но если рассматривать сам факт наличия условной проверки как проблему, то тогда практически любой нетривиальный код начнет выглядеть “подозрительно”

Слишком много пустых разговоров там, где достаточно было бы сухих цифр. Вы у себя в статье привели в пример код метода ParseHeaders. Вот и озвучили бы замеры – без trace points вообще и с отключенными trace points.

Всё тело then - это один вызов функции.

Всё тело then - это один вызов функции.

Полагаю, что вот в этом месте:

  LogmeTPt(
    "begin header parsing: client=%s bytes=%zu"
    , connection->GetIp().c_str()
    , buffer.Size()
  );

Он все-таки не один. Тут есть еще вызовы GetIp(), c_str(), Size().

И еще неизвестно во что разворачивается сам LogmeTPt.

Upd. А разворачивается LogmeTPt отнюдь не совсем в безобидную конструкцию. Там декларация статической переменной, вызов Hit для нее (всегда), проверка флага Registered… И только потом собственно логирование, если оно разрешено.

не будет никакого разворачивания параметров если трейс поинт не активирован явно. это изложено в статье. это макро, а не вызов функции. если логирование отключено полностью, превратится в ничто. параметры типа GetIp() будут вычисляться только когда они будут реально использоваться для вызова физической функции. в случае if (condition) fn(..., GetIP()...) это не произойдет если condition=false (а это как раз то что будет с отключенной трейс поинт)

мало того, если указан канал явно и он отключен, или уровень логирования выше чем использованном макро, то и в этом случае вызова GetIP не будет вообще. если интересно, то можете найти информацию об этом тут - https://tips.efmsoft.com/ru/логирование-тормозит-систему-всегда/

не будет никакого разворачивания параметров

Здесь речь идет о том, что наличие LogmeTPt превращает код простой функции:

void f() {
  LogmeTPt(...);
  bla-bla-bla;
}

во что-то вида:

void f() {
   {
      static Logme::TracePoint tracePoint(__FILE__, __FUNCTION__, __LINE__);
      tracePoint.Hit();
      if (!tracePoint.Registered)
        Logme::Detail::RegisterTracePointOnce(tracePoint);
      if (tracePoint.Enabled && Logme::Instance->Condition())
      {
        static Logme::ContextCache contextCache;
        dispatch(Logme::Instance, contextCache, level, &CH, &SUBSID, __FUNCTION__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__));
      }
    } while (0)}
   bla-bla-bla;
}

и если вы хотите сказать, что все это разбухание функции f() из-за единственного LogmeTPt – это все совершенно бесплатно, то мне остается еще раз повторить: приведите цифры, пожалуйста.

Цена есть у первоначального вызова.

После него цена каждого холостого вызова - меньше одного цикла, так как компилятор добавляет проверку скрытого флага и она всегда не выполняется.

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

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

Пусть будут хотя бы какие-то цифры. Но их нет вообще. Этого, собственно, в статье и не хватило.

так вас производительность волнует или разбухание?

так вас производительность волнует или разбухание?

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

И еще неизвестно во что разворачивается сам LogmeTPt.

предлагаю посмотреть реальный код если заботит проблема производительности

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

прошу обратить внимание, что статья была не о производительности.

Вы это уже говорили. Но тут появилась еще одна ветка обсуждения. Претензии к тому, кто ее начал :)

были обозначены проблемы и пути их решения.

С этим как раз все понятно.

почему бы не поговорить о сути статьи?

Потому что лично для меня суть не раскрыта полностью без обсуждения накладных расходов. Ваш основной тезис – trace points можно расставлять в коде по надобности и не сильно заботиться о том, что они негативно сказываются на производительности. Зато в любой момент их можно активировать и увидеть то, что поможет найти проблему. С этим OK ровно до тех пор, пока накладные расходы реально низкие. А вот так это или нет… Предлагаете праздному читателю самому скачать вашу библиотеку и устроить бенчмарк?

Sign up to leave a comment.

Articles