Бенчмаркинг и сравнение производительности
Краткое резюме
Бенчмаркинг — это эксперимент, а не «запустить wrk на 5 минут». Главные принципы:1. Сформулируйте гипотезу и метрики.
2. Контролируйте переменные (железо, ядро, питание, шум фона).
3. Собирайте достаточно данных (реплики, доверительные интервалы).
4. Проводите профилирование — без него не понять «почему».
5. Делайте repro: скрипты, фиксация версий и артефактов.
Цели бенчмарка и бизнес-метрики
Пропускная способность (throughput): RPS/QPS/CPS, записи/сек.
Задержка (latency): p50/p95/p99/плотность хвостов.
Эффективность: Cost-per-1k RPS, ватт на транзакцию, $/миллисекунду улучшения.
Стабильность: джиттер, вариативность между циклами/нодами.
Эластичность: как масштабируются показатели при N× ресурса (Amdahl/Gustafson ориентиры).
Методология: дизайн эксперимента
Гипотеза: «Envoy с HTTP/3 уменьшит p95 TTFB на 10–15% при том же RPS».
Единица сравнения: версия билда/конфиг/инстанс железа.
A/B-схема: параллельный прогон на идентичном окружении; либо ABAB/Latin Square для снижения влияния дрейфа.
Количество повторов: ≥ 10 коротких + 3 длинных прогона на конфигурацию для устойчивых оценок.
Статистика: медиана, MAD, доверительные интервалы бутстрэпом; непараметрические тесты (Mann–Whitney) для «хвостатых» распределений.
DoE (минимально): меняйте одну переменную за раз (OVAT) или фракционный факторный план для 2–3 факторов (например, TLS-профиль × HTTP-версия × ядро).
Контроль переменных и шумов
CPU governor: `performance`; отключить «power save».
Turbo/Throttling: мониторинг частот, температур и троттлинга (иначе прогрев даст ложные выигрыши).
NUMA/Hyper-Threading: закрепляйте IRQ и процессы (`taskset/numactl`), измеряйте локальность памяти.
C-states/IRQ balance: фиксируйте настройки; для сетевых тестов — pin IRQ на конкретные ядра.
Фоновые процессы: чистая нода, выключить cron/backup/antivirus/updatedb.
Сеть: стабильные пути, фиксированные MTU/ECN/AQM, отсутствие флаттера канала.
Данные: одинаковые наборы, кардиналитет и распределения.
Кэш: разделяйте «холодный» (первый проход) и «теплый» (повторный) режимы, явно помечайте.
Классы бенчмарков
1) Микро-бенчмарки (функция/алгоритм)
Цель: измерить конкретный код/алгоритм.
Инструменты: встроенные бенч-фреймворки (Go `testing.B`, JMH, pytest-benchmark).
Правила: прогрев JIT, миллисекунды → наносекунды; изоляция GC; фиксированный seed.
2) Мезо-бенчмарки (компонент/сервис)
HTTP-сервер, кэш, брокер, БД на одной ноде.
Инструменты: wrk/wrk2, k6 (open model), vegeta, ghz (gRPC), fio, sysbench, iperf3.
Правила: лимиты соединений/файлов, пулов; отчет о CPU/IRQ/GC.
3) Макро-бенчмарки (e2e/путь запроса)
Полный путь: CDN/edge → proxy → сервис → БД/кэш → ответ.
Инструменты: k6/Locust/Gatling + RUM/OTel трейсинг; реалистичная смесь маршрутов.
Правила: ближе к реальности («грязные» данные, лаги внешних систем), аккуратно с ретраями.
Набор метрик по слоям
Шаблоны тестов и команды
Сеть (TCP/UDP):bash iperf3 -s # server iperf3 -c <host> -P 8 -t 60 # parallel, stable bandwidth
HTTP-сервер (стабильная нагрузка, wrk2):
bash wrk2 -t8 -c512 -d5m -R 20000 https://api. example. com/endpoint \
--latency --timeout 2s
Open-модель (k6, arrival-rate):
javascript export const options = {
scenarios: { open: { executor: 'constant-arrival-rate', rate: 1000, timeUnit: '1s',
duration: '10m', preAllocatedVUs: 2000 } },
thresholds: { http_req_failed: ['rate<0. 3%'], http_req_duration: ['p(95)<250'] }
};
Диск (fio, 4k random read):
bash fio --name=randread --rw=randread --bs=4k --iodepth=64 --numjobs=4 \
--size=4G --runtime=120 --group_reporting --filename=/data/testfile
БД (sysbench + PostgreSQL примерная идея):
bash sysbench oltp_read_write --table-size=1000000 --threads=64 \
--pgsql-host=... --pgsql-user=... --pgsql-password=... prepare sysbench oltp_read_write --time=600 --threads=64 run
Память/CPU (Linux perf + stress-ng):
bash perf stat -e cycles,instructions,cache-misses,L1-dcache-load-misses \
-- <your_binary> --bench
Статистика и валидность
Повторы: минимум 10 прогонов, исключить outliers (робастно: медиана/MAD).
Доверительные интервалы: бутстрэп 95% CI для p95/p99 и средних.
Эффект-размер: относительное изменение и его CI (напр., −12% [−9%; −15%]).
Практическая значимость: уменьшение p95 на 10% при цене +30% CPU — стоит ли?
Графики: violin/ECDF для распределений, «кривые насыщения» (RPS→latency).
Профилирование и локализация узкого места
CPU: `perf`, `async-profiler`, eBPF/pyroscope; flamegraph до и после.
Alloc/GC: профили runtime (Go pprof/Java JFR).
I/O: `iostat`, `blktrace`, `fio --lat_percentiles=1`.
Сеть: `ss -s`, `ethtool -S`, `dropwatch`, `tc -s qdisc`.
БД: `EXPLAIN (ANALYZE, BUFFERS)`, pg_stat_statements, slowlog.
Кэш: топ-ключи, TTL, eviction причина.
Отчетность и артефакты
Что фиксировать:- git SHA билда, флаги компиляции/оптимизации.
- Конфиги ядра/сети (sysctl), версии драйверов/NIC/firmware.
- Топологию (vCPU/NUMA/HT), governor, температура/частоты.
- Данные: размер, кардиналитет, распределения.
- Что публиковать: таблицы p50/p95/p99, ошибка/сек, throughput, ресурсы (CPU/RAM/IO), CI.
- Артефакты: скрипты прогона, графики, flamegraph, сырой JSON/CSV результатов, протокол окружения.
Честные сравнения (fair benchmarking)
Идентичные ограничители (conn pool, keepalive, TLS цепочки, OCSP stapling).
Согласованные таймауты/ретраи и HTTP версия (h2/h3).
Температурный баланс: прогрев до равновесия (без turbo-буст эффекта).
Справедливые кэши: либо оба «холодные», либо оба «теплые».
Сетевая симметрия: одинаковые маршруты/MTU/ECN/AQM.
Бюджет времени: DNS/TLS/connect — считать явно или исключать одинаково.
Анти-паттерны
Один прогон → «вывод».
Смешение режимов (часть холодных, часть теплых) в одной серии.
Закрытая модель вместо открытой для интернет-нагрузки → ложная «устойчивость».
Неучтенные ретраи → «RPS растет» ценой дублей и каскадных 5xx.
Сравнение на разных железках/ядрах/энергосхемах.
Отсутствие профилирования → оптимизация «вслепую».
Игра с GC/heap без анализа профилей → регрессии хвостов.
Практические рецепты
Шаги минимального бенч-пайплайна:1. Зафиксировать окружение (скрипт `env_capture.sh`).
2. Прогреть (5–10 мин), зафиксировать частоты/температуры.
3. Провести N повторов коротких + 1 длинный прогон.
4. Снять профили (CPU/alloc/IO) на пике.
5. Посчитать CI/графики, собрать артефакты.
6. Решение: принять/отклонить гипотезу, сформировать next steps.
Кривая емкости (capacity curve):- Ступени RPS (10% шага) → фиксируем p95/ошибки → находим «колено».
- Строим график RPS→latency и RPS→CPU: видим границу и стоимость дальнейших %.
Специфика для iGaming/финтех
Стоимость миллисекунды: ранжируйте улучшения по $-эффекту (конверсия/отток/лимиты PSP).
Пики (матчи/турниры): бенчмарки spike + plateau с прогревом TLS/CDN/кэшей.
Платежи/PSP: измеряйте end-to-end с sandbox-лимитами, идемпотентность и реакции на деградации; фиксируйте Time-to-Wallet прокси-метриками.
Антифрод/бот-фильтры: включайте в макро-бенч профиль правил (false-positive-rate, latency добавка).
Лидеры/джекпоты: тестируйте горячие ключи/ранжирование, блокировки, атомарность.
Чек-лист бенчмаркинга
- Гипотеза/метрики/критерий успеха.
- Контроль переменных (питание/NUMA/IRQ/сеть/кэш).
- План прогонов (реплики, длительность, прогрев).
- Разделение «холод/теплый» режимов.
- Профилирование включено (CPU/alloc/IO/БД).
- Статистика: CI, тесты значимости, графики.
- Артефакты и repro-скрипты в репозитории (IaC для стенда).
- Отчет с «стоимостью улучшения» и рекомендациями.
- Ретест после оптимизаций (regression perf).
Мини-отчет (шаблон)
Цель: уменьшить p95 API на 15% без роста CPU>10%.
Метод: A/B, k6 open-model 1k rps, 10×3 прогона, warm cache.
Итого: p95 −12% [−9%; −15%], CPU +6%, 5xx без изменений.
Flamegraph: ↓ JSON сериализация (−30% CPU), узкое место сместилось в БД.
Решение: принять оптимизацию; следующий шаг — батчирование запросов БД.
Артефакты: графики, профили, конфиги, сырой JSON.
Итог
Хороший бенчмаркинг — это строгая методология + честные сравнения + статистическая валидность + профилирование + воспроизводимость. Ставьте гипотезы, контролируйте окружение, считайте доверительные интервалы, публикуйте артефакты и принимайте решения по стоимости улучшения. Так вы получите не красивую цифру в презентации, а реальный прирост скорости и предсказуемости платформы.