Привет, Хабр!
Все мы любим быстрые программы и высокие показатели в бенчмарках. Когда гоняешь тесты производительности, так и тянет включить все оптимизации компилятора, чтобы выжать максимум. Но если вы имели дело с пакетами тестов SPEC (например, SPEC CPU), то, вероятно, замечали, результаты там делятся на две категории Base и Peak.
В тестах SPEC CPU есть концепция базового прогона (base run) и пикового (peak run). Это строго определенные режимы с разными правилами оптимизации. Base про честность и сопоставимость, Peak про максимальную производительность любой ценой (ну, почти любой).
Base: один набор флагов для всех
Идея base‑run проста, все бенчмарки компилируются и запускаются с единым набором настроек, без индивидуального тюнинга под каждый тест. Представьте, что вы обычный пользователь, который хочет по‑честному оценить железо: вы взяли компилятор, включили приличный уровень оптимизации (скажем, ‑O2 или ‑O3) и прогнали весь пакет тестов. Вы же не будете подгонять каждый из 40+ бенчмарков отдельными ключами, верно? Base‑режим призван отражать такой подход.
Правила Base: один конфиг правит всем. В техническом плане это означает:
Одинаковые опции компиляции для всех программ внутри набора (например, для всех С++‑тестов используется один и тот же набор флагов, для всех Си свой, но тоже единый). Никаких индивидуальных ключей под конкретный бенчмарк.
Только один этап компиляции. Запрещены многоступенчатые схемы вроде профилирующей оптимизации, когда вы сначала собираете программу, запускаете ее на тренировочном наборе, затем повторно оптимизируете под собранный профиль. В Base так нельзя, только прямой компил.
Только безопасные, стандартизованные оптимизации. Допускаются лишь те флаги компиляции, которые не рискуют нарушить корректность работы программы и соответствие языковому стандарту.
Последний пункт самый интересный. Что значит не нарушают корректность? Речь о тех оптимизациях, которые жертвуют точностью расчетов или строгим соблюдением стандартов ради скорости. Классический пример флаг -ffast-math в GCC/Clang. Он включает ряд ухищрений для ускорения операций с плавающей запятой: может игнорировать проверку на NaN/Inf, считать ассоциативность сложения допустимой (то есть выражения (a+b)+c и a+(b+c) приравниваются, хотя по IEEE 754 это неэквивалентно), отключать некоторые проверки и так далее Да, с -ffast-math программы обычно работают ощутимо быстрее, но могут чуть потерять в точности или вообще дать иной результат в крайних случаях. Поэтому в Base‑режиме SPEC такое не разрешается. Бенчмарк должен выполняться по правилам, а не самым быстрым, но сомнительным способом.
Какие еще оптимизации под запретом в Base? Прямо полного списка нельзя нет, но по сути любая опция, которая может ухудшить точность, повторяемость или переносимость результатов, не приветствуется. Примеры:
Отключение обработки исключений или RTTI в C++.
Кардинальное изменение модели вычислений. Скажем, принудительная замена 64-битных операций на 32-битные тоже мимо. Разве что сами тесты укладываются в 32 бита, но это отдельная история со специальными разрешениями.
Подмена целых алгоритмов на сторонние реализации. Например, заставить бенчмарк линковаться с вашей супер‑оптимизированной библиотекой BLAS или FFT вместо той, что встроена в код. В Base этого делать нельзя (а вот в Peak, пожалуйста, если библиотека общедоступна и вы честно заявите об этом в отчете).
Цель всех этих ограничений получить некий базовый уровень производительности, который достижим. Именно его и сравнивают между разными системами. Вендоры компиляторов и процессоров не могут специально заточить компиляцию под один единственный тест, им приходится выбирать такой набор флагов, который хорош в среднем для всех. В итоге Base‑результаты обычно ниже Peak‑результатов, зато считаются более честными. По сути, Base отвечает на вопрос: «Какую производительность даст эта система при типичном хорошем, но не экстремальном уровне оптимизации?»
Peak: включаем все возможные оптимизации на максимум
Peak‑режим другое дело. Тут позволено применять все допустимые средства оптимизации, чтобы продемонстрировать максимальный возможный результат. Каждому бенчмарку свой подход, лишь бы было быстрее и в рамках правил SPEC.
Что можно в Peak, чего нельзя в Base:
Разные флаги для разных программ. Можно компилировать каждый тест со своим набором опций. Например, какой‑нибудь вычислительно тяжелый Fortran‑бенчмарк сдобрить особыми флагами для векторизации, а небольшой C++ код дополнительно включить агрессивное разворачивание циклов. Главное, все эти флаги должны быть официально доступными и задокументированными.
Профилирование и итеративная компиляция. Профиль‑ориентированная оптимизация в Peak разрешена. SPEC обычно предоставляет специальный тренировочный набор входных данных, вы можете прогнать его, собрать профиль, потом заново скомпилировать бенчмарк с учетом этого профиля.
Даже рискованные флаги — можно. В Peak допускаются агрессивные оптимизации, влияющие на точность, если итоговые результаты проходят проверку корректности. То есть, даже
-ffast-mathили аналогичные опции можно применять, но на ваш страх и риск, если из‑за них программа не сойдет по валидации, результат никто не зачтет. Также, все подобные флаги должны быть перечислены в отчете.
По сути, Peak отвечает на вопрос: «Какую наивысшую производительность можно выжать из системы, если тщательно оптимизировать под каждый конкретный тест?» Здесь в ход идут все уловки. Например, в Peak‑отчетах часто видно, как компании указывают длиннющие списки флагов для разных бенчмарков, вплоть до специальных параметров для управления кэшированием или разного числа потоков для разных нагрузок.
Почему -ffast-math и коллеги запрещены в Base
Чуть подробнее про запретные флаги на примере -ffast-math. Этот флаг часто дает ощутимый прирост в вычислительных задачах за счет упрощения модели вычислений с плавающей точкой. Казалось бы, почему бы не позволить его в Base? Причина в том, что SPEC хочет гарантировать корректность и воспроизводимость результатов. Base‑режим должен быть такой точкой отсчета, где у всех участников равные условия и никто не жертвует точностью ради выигрыша в скорости.
Допустим, одна команда включила -ffast-math и ее результат на каком‑нибудь тесте вырос на 20%. А другая команда, соблюдая осторожность, этого не сделала и показала чуть меньший результат, зато абсолютно точный. Сравнение было бы нечестным,первая фактически получила преимущество ценой потенциальной потери корректности, даже если тесты SPEC формально прошли.
Зато в Peak пожалуйста, хотите рискнуть с -ffast-math или подобными флагами, рискуйте. Только потом в отчете честно перечислите все примененные ключи и трюки, чтобы эксперты могли понять, что вы там настроили. И, конечно, будьте уверены, что не сломали валидность результатов, иначе какой смысл.
Портировочные флаги vs оптимизационные
Стоит упомянуть, что есть исключение из строгого правила «все флаги едины». Речь о так называемых портировочных флагах (portability flags). Это опции, которые нужны, чтобы конкретный тест вообще скомпилировался или работал на данной платформе, но они не влияют на производительность. Например, указать нестандартный заголовочный файл, отключить определенную проверку или включить поддержку устаревшего режима вычислений, если код теста этого требует. Такие флаги можно (и нужно) использовать, и они могут различаться от бенчмарка к бенчмарку даже в Base. SPEC заранее утверждает список допустимых портировочных опций. Главное: они не должны давать выигрыша в скорости, только обеспечивать успешную сборку/запуск.
В отчете SPEC все флаги делятся на портировочные и оптимизационные. Поэтому если вы видите, что в разделе Base у разных тестов были разные ключи, скорее всего, это именно portability (и это ок). А вот оптимизационные одинаковы для всех, иначе результат не сочтут базовым.
Зачем это нужно
Как правило, Base‑результаты ниже Peak‑результатов на 5–30%, в зависимости от системы и компилятора. Иногда разница небольшая, а иногда включение специальных оптимизаций в Peak дает внушительный прирост. Например, профилирование способно ускорить некоторые программы на 10–15%. А ручная подстройка флагов под каждый тест может выжать дополнительные проценты там, где единый набор флагов в Base вынужден быть более консервативным.
Когда производители публикуют результаты SPEC, они обычно показывают оба значения. Base служит для сравнения в равных условиях. А Peak для демонстрации максимально достижимого уровня производительности (чаще в маркетинговых целях, чтобы впечатлить красивыми цифрами).
Даже Peak подчиняется некоторым правилам. Нельзя, скажем, переписать исходники тестов, код должен выполняться как есть. Нельзя использовать неопубликованные экспериментальные компиляторы. Все примененные оптимизации должны быть доступными и поддерживаемыми для обычных пользователей (например, использовать специальную внутреннюю версию компилятора, которой нет в открытом доступе, запрещено). То есть SPEC пытается балансировать, позволить выкрутить настройки на максимум, но так, чтобы это всё еще отражало реальные возможности, а не какие‑то приёмы, недоступные конкурентам.
Выводы
Base — это строгость и унификация, Peak — простор для творческого разгона. Оба показателя по‑своему важны.
Успехов в оптимизации!

После разговора о Base/Peak почти неизбежно возникает знакомая боль: в бенчмарках всё чисто и предсказуемо, а в проде — трафик скачет, кластеры плывут, кеш-блоки живут своей жизнью. Синтетика не спасает, когда система работает на пределе и любая неучтённая деталь начинает стоить миллисекунд, денег и сна.
Если хочется понять, как выглядит реальная производительность за пределами аккуратных SPEC-графиков, в рамках курсов OTUS пройдут бесплатные демо-уроки, где практики разберут highload на живых примерах:
17 декабря 20:00 — Инструменты и механизмы Kubernetes для обеспечения высокой нагрузки. Записаться
22 декабря 20:00 — Проектирование высоконагруженного мониторинга в распределённых системах. Записаться
23 декабря 20:00 — Cache friendly код: оптимизируем работу с памятью. Записаться