Active Benchmarking: как делать бенчмарки правильно

Дебаг бенчмарков — то, чем я занимаюсь много лет, и я повидал поразительное и местами комичное разнообразие способов, которыми бенчмарки идут не так. Проблема в том, что бенчмарк-инструменты часто запускают, не понимая, что именно они тестируют, и не проверяя, что результаты валидны. Это ведёт к плохим архитектурным и продуктовым решениям, которые потом преследуют команду годами.

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

Что такое active benchmarking

Active benchmarking — это методика, при которой пока тест идёт, вы активно наблюдаете систему: смотрите на метрики ОС, профилируете процессы, инспектируете latency, смотрите на флеймграфы. Цель — доказать, что бенчмарк действительно меряет то, что вы предположили.

Противоположность — passive benchmarking: «запустил тулу, скопировал число из вывода, нарисовал слайд». Подавляющее большинство опубликованных бенчмарков — пассивные. И в подавляющем большинстве из них есть проблема, незамеченная автором.

Что обычно идёт не так

1. Вы измеряете не то, что думаете

Классика: бенчмарк сети между двумя серверами выдаёт 200 Мбит/с. Кто-то делает вывод, что сеть медленная. На самом деле это была производительность инструмента (одного потока iperf), а сеть свободно пропускает 10 Гбит/с при правильной нагрузке.

Правило: пока бенчмарк работает, проверьте, упирается ли он действительно в то, что вы тестируете. Если тестируете CPU — посмотрите, что CPU действительно занят на 100%. Если диск — что IOPS реально упёрся в потолок устройства, а не в ограничение приложения.

2. Бенчмарк ограничен чем-то невидимым

  • Single-threaded инструмент тестирует параллельную систему.

  • Локальный диск-IO упирается в page cache, а не в реальный SSD.

  • TCP-окно слишком маленькое для long-fat сети.

  • Network bandwidth ограничен NIC offloading, а не link speed.

Каждый из этих случаев даёт «реалистично выглядящий» результат, который не отражает реальный потолок.

3. Warm-up и steady state

Первые секунды любого бенчмарка — это разогрев: JIT-компиляция, заполнение кэшей, инициализация пулов соединений. Если ваш бенчмарк короткий, вы измеряете в основном разогрев. Active benchmarking подразумевает: «запустить, дождаться steady state, потом начать измерения».

4. Coordinated omission

Классическая ловушка load-тестов: когда сервер тормозит, клиент ждёт ответа и не отправляет следующий запрос. В результате latency-гистограмма выглядит хорошо — потому что медленные запросы, которые должны были быть отправлены в эту секунду, просто не были отправлены. Реальная latency, которую увидит юзер в продакшене, намного хуже.

Лечится HdrHistogram с компенсацией coordinated omission или использованием тулзы с открытой моделью нагрузки (open loop — например, wrk2).

Practical workflow

Шаг 1: сформулируйте гипотезу

Прежде чем запускать бенчмарк, ответьте себе:

  • Что именно я хочу узнать? («Способна ли база выдержать 50K RPS чтения с этим железом?»)

  • Какие компоненты системы должны быть бутылочным горлышком в этом тесте? («БД CPU, не сеть, не диск».)

  • Какой результат я ожидаю и почему?

Если вы не можете назвать ожидание — вы не понимаете, что тестируете.

Шаг 2: подготовьте observability

Запустите параллельно с бенчмарком:

  • vmstat 1 / pidstat 1 — общая картина CPU, IO, контекст-свитчей.

  • iostat -x 1 — диск.

  • sar -n DEV 1 или nload — сеть.

  • perf stat -a или perf top — что именно жрёт CPU.

  • Для конкретного процесса — flamegraph (Brendan Gregg’s own tool: FlameGraph).

  • dstat, nicstat, tiptop, lsof, tcpdump — по необходимости.

Шаг 3: запустите бенчмарк, держа observability открытым

Во время теста смотрите на метрики. Сверяйте с гипотезой:

  • «Я думал, БД должна быть CPU-bound. Покажет ли top 100% на её PID?»

  • «Я думал, мы упрёмся в диск. Покажет ли iostat device utilization 100%?»

  • «Я думал, network должна быть свободна. sar -n DEV показывает <10% — да, как и ожидалось.»

Если реальность не соответствует гипотезе — либо ваша гипотеза неправильная (полезно узнать), либо ваш бенчмарк меряет не то (тоже полезно узнать).

Шаг 4: разоблачите тулзу

Каждый бенчмарк-инструмент имеет ограничения. Прежде чем доверять цифре, изучите:

  • Single-threaded или multi-threaded?

  • Open или closed loop по нагрузке?

  • Как считается latency — wall-clock или CPU?

  • Что инструмент не учитывает (пример: iperf не учитывает TCP retransmits)?

Лучшие тулзы (fio, wrk2, sysbench с правильными опциями) дают достоверные цифры. Многие popular-тулзы — нет.

Главное

Бенчмарк без active observability — это анекдот, выдающий себя за данные. Если вы не можете во время теста показать, почему система упирается именно туда, куда упирается, ваш результат может быть просто артефактом инструмента, кэша, или пиковой неудачи. Лучшая защита — методичность: гипотеза → observability на месте → тест → сверка → выводы.

Эта методика — не теоретическая. В Netflix мы её ежедневно применяли, когда выясняли, почему продакшен-инстанс EC2 показывает другую производительность, чем dev-машина. Раз за разом оказывалось: ответ не в железе, а в неточно понятых ограничениях бенчмарка.