понедельник, 4 мая 2026 г.

[prog] Любопытное из книги "C++ Ultra-Low Latency: Multithreading and Low-Level Optimizations"

Попалась в руки книга "C++ Ultra-Low Latency: Multithreading and Low-Level Optimizations". Начал ее листать, т.к. темой низкоуровневых оптимизаций на C++ никогда не занимался. Мне всегда было интересно писать корректно работающий код, который был бы понятным и сопровождабельным, который бы было просто использовать правильно, но сложно неправильно, но в плане скорости работы кода никогда не упарывался. В общем, как однажды сказали про мой код: "получение гарантий корректности времени компиляции при этом не используя Haskell" 🙂

Решил немного просветиться на эту тему выжимания производительности, говорят, что учиться никогда не поздно.

Про саму книгу ничего не скажу, только начал с ней знакомиться, а начало тупо пропустил, т.к. там много говорится о специфике HFT, а к HFT вообще не имею никакого отношения. Перешел сразу к главам, где про конкретные приемы рассказывается. И вот в разделе про Branch Prediction наткнулся на вещи, которые мне прям как бальзам на душу, а именно:

Дело в том, что для меня всегда наиболее естественно было писать в стиле:

if(some_condition) {
  ... // тут много строчек кода с выполнением основной логики.
  ...
  ...
}
else {
  return some_error_code;
}

Т.е. большинство действий сосредотачивается именно в ветке then.

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

if(!some_condition) {
  return some_error_code;
}
else {
  ... // тут много строчек кода с выполнением основной логики.
  ...
  ...
}

Или даже вот такой:

if(!some_condition) {
  return some_error_code;
}

... // тут много строчек кода с выполнением основной логики.
...
...

Но оба эти стиля мне не нравятся на каком-то интуитивном уровне. Особенно последний (про этот стиль я уже высказывался: например, в контексте языка Go). Хотя, если мы в проекте придерживаемся принципов defensive programming, то начало метода/функции из if-ов для проверки входных параметров/состояния объекта, т.е. что-то вроде:

int f(int a, int b, int c) {
  if(a < 0) return invalid_parameter_a;
  if(b < 10 || b > 100) return invalid_parameter_b;
  if(c > 1000) return invalid_parameter_c;

  ... // Далее основная логика.
}

то такие короткие if-ы -- это нормально. Но когда проверки входных данных завершены и идет основной код метода/функции, то if-ы с короткими then или if-ы, в которых только return, на мой взгляд, ухудшают код (хуже только циклы, внутри которых короткие if-ы с continue).

И вот листая книгу "C++ Ultra-Low Latency: Multithreading and Low-Level Optimizations" вдруг натыкаюсь на подтверждение того, что привычный для меня способ написания if-ов имеет под собой обоснование еще и с точки зрения эвристик компилятора по обеспечению branch predictions.

Комментариев нет: