Цена операций в процессорных тактах

Авторы Игнатченко и Иванчикин опубликовали вторую часть главы 4 из готовящейся книги про эффективный C++. В ней они собрали известные цены основных операций в тактах процессора, которые часто нужны разработчикам, чтобы понять, где искать узкие места в коде.
Деление остаётся дорогим: на современных 64-битных CPU оно занимает около 15 тактов, тогда как умножение - всего 3-5. Исключения обходятся в 2700-5000 тактов при возникновении, но если ошибки редкие (раз на 10000 вызовов и реже), они дешевле, чем проверка кодов ошибок. Контекстные переключения потоков стоят 10000-100000 тактов в прямых затратах, но косвенные потери из-за инвалидации кэша достигают миллионов тактов.
Ключевые факты
- Деление на 64-битных CPU занимает 15 тактов, в многосокетных системах может доходить до 600 тактов
- Умножение и деление остаются дорогими операциями, все остальные целочисленные операции - 1-2 такта
- Исключения при редких ошибках (раз на 10000+ вызовов) эффективнее кодов ошибок, но если ошибка происходит, стоит 2700-5000 тактов
- Вызовы функций обходятся в 15-30 тактов в прямых затратах, виртуальные вызовы дороже - 30-60 тактов
- Контекстные переключения стоят 10000-100000 тактов прямых затрат, косвенные потери из-за кэша достигают 3 миллионов тактов
Почему это важно
Разработчики часто полагаются на микробенчмарки, которые дают искажённую картину реальной производительности. Точные числа операций в тактах помогают предсказать поведение кода без запуска тестов. Эта информация особенно ценна при оптимизации критических участков, когда нужно выбрать между несколькими подходами: например, стоит ли избегать деления, если оно встречается редко, или лучше переписать весь алгоритм.
Кому это важно
Разработчикам на C++ и системных программистам, которые оптимизируют производительность под конкретные процессоры. Авторы библиотек, работающие с критичными по скорости модулями. Инженеры, которые проектируют многопоточные системы и должны понимать цену контекстных переключений.
Как это применить
При профилировании кода используйте эту таблицу как первый ориентир: если в критичной функции есть деления, рассмотрите их переписывание. Для исключений проверьте частоту их возникновения - если ошибки чаще, чем раз на 10000 вызовов, лучше вернуть код ошибки. Для многопоточного кода минимизируйте контекстные переключения, потому что косвенные потери от инвалидации кэша могут быть в 10-100 раз больше прямых затрат.
Можно ли доверять
Это черновик будущей книги, авторы сами указывают, что все числа точны только с точностью до порядка величины. Они опираются на авторитетные источники (Agner Fog, университетские исследования), но признают, что часть данных может быть устаревшей и нуждается в собственном тестировании. Разработчики приглашают читателей указывать на ошибки.
Риски и подводные камни
Числа варьируются в зависимости от архитектуры и даже поколения CPU - то, что верно для x64 Skylake, может не работать для ARM. Косвенные потери от контекстных переключений зависят от паттернов доступа в памяти, и в худшем случае (полностью случайный доступ) могут достигать 30 миллионов тактов. Инвалидация кэша часто скрыта и не видна в простых бенчмарках.