awesome-everything EN
↑ Обратно к восхождению

Производительность

История профайлеров и ловушки микробенчей: от Кнута до GWP

Суть Родословная от Амдала до GWP объясняет ограничения профилирования. Микробенчи неверны: JIT warmup, dead-code elimination, свёртка констант.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 18 min

Дефолтный warmup JMH для Java — 5 итераций. Он существует не ради производительности — он нужен потому, что без него твой бенчмарк измеряет интерпретатор, а не production JIT. Большинство команд, впервые самостоятельно пишущих микробенчмарки, делают эту ошибку с первого прогона.

Интеллектуальная родословная: от Амдала до GWP

Дисциплина «профиль первым» имеет 60-летнюю родословную.

Джин Амдал, 1967 — «Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities». Показал, что последовательная часть нагрузки ограничивает ускорение, которое способно дать любое количество параллельных процессоров. Исходный аргумент за измерение доли перед оптимизацией.

Дональд Кнут, 1974 — «Structured Programming with Go To Statements», ACM Computing Surveys. Ввёл формулировку «преждевременная оптимизация — корень всех зол», с полным предложением, называющим разделение 97/3: большая часть кода не имеет значения; критическая доля имеет, и её идентификация — это и есть инженерная работа.

Джон Густафсон, 1988 — «Reevaluating Amdahl’s Law». Утверждал, что по мере роста размера задач параллелизуемая доля растёт, так что пессимизм Амдала занижает достижимое ускорение на реальных нагрузках. Закон Густафсона: масштабированное_ускорение = s + p × N, где N — количество процессоров, а нагрузка растёт вместе с N.

Google Wide Profiling (GWP), 2010 — Рен и соавторы, IEEE Micro. Показали непрерывное профилирование всего датацентра при накладных расходах менее 0.01% с помощью статистического мультиплексирования. Эта техника стала Pyroscope, Parca, Polar Signals и категорией непрерывного профилирования.

Дуга: идентифицируй узкое место (Амдал 1967) → назови это дисциплиной (Кнут 1974) → уточни модель при масштабировании (Густафсон 1988) → сделай измерение бесплатным и постоянным (GWP 2010, Pyroscope 2020-е). Каждый слой добавил ограничение, ставшее стандартной практикой.

Ловушки микробенчей в JIT-рантаймах

Наивные микробенчи почти по умолчанию неверны в JIT-компилируемых рантаймах (JVM, V8, .NET CLR, PyPy).

Проблема JIT warmup. Ступенчатая компиляция HotSpot: ~10k вызовов для C1 (базовый), ~100k для C2 (оптимизированный). Микробенч, вызывающий функцию 1k раз, измеряет интерпретатор или базовый JIT, а не оптимизированный код, работающий в продакшене. JMH справляется с этим явными warmup-итерациями (по умолчанию 5 итераций × 10 с каждая до начала измерения).

Устранение мёртвого кода. Если результат бенчмарка не используется, оптимизатор полностью удаляет тело цикла. Цикл выполняется за микросекунды и репортирует невозможное ускорение. testing.B в Go требует записи результата в sink-переменную пакета (var sinkResult = result). JMH использует Blackhole.consume(result).

Свёртка констант. Если входные данные цикла — константы времени компиляции, оптимизатор вычисляет ответ один раз и заменяет цикл литералом. Цикл, вычисляющий md5("fixed-string") 1M раз, может свернуться в единственную загрузку константы. Решение: параметризовать входные данные во время выполнения из непостоянного источника (JMH @Param, Go benchmark + внешние данные).

Различия в инлайнинге. Микробенч может инлайнить функцию, которую продакшен не инлайнит (или наоборот), потому что дерево вызовов бенчмарка проще. Аннотации @CompilerControl в JMH позволяют принудительно включать или отключать инлайнинг для соответствия production-поведению.

Масштабирование частоты CPU. CPU ноутбуков агрессивно троттлятся: время бенчмарка функции варьируется на 30% между холодным стартом и троттлингом. Production-железо имеет другие политики частоты. Всегда бенчмаркируй на представительном железе с отключенным масштабированием частоты или зафиксированными тактами.

ЛовушкаСимптомФикс
JIT warmupБенчмарк в 3-10x медленнее продакшенаЯвный warmup (JMH), B.ResetTimer() после прогрева в Go
Устранение мёртвого кодаБенчмарк завершается за наносекунды — подозрительно быстроКонсьюмить результат через sink-переменную или Blackhole
Свёртка константВремя инвариантно к размеру входных данныхПараметризовать входные данные во время выполнения, не на этапе компиляции
Различия в инлайнингеБенч в 2x быстрее продакшена@CompilerControl для принудительного/запрещённого инлайнинга
Масштабирование частоты CPUВысокая дисперсия между прогонами на ноутбукеЗафиксировать такты CPU, бенчмаркировать на серверном железе
Почему это работает

Промышленные фреймворки (JMH для Java, criterion.rs для Rust, benchstat в Go) стандартизируют прогрев, запускают несколько итераций и репортируют статистические сводки с предупреждениями о дисперсии. Написание микробенчмарка с нуля в спешке — самодельный цикл таймирования — обнаружит одну или несколько описанных выше ловушек. Используй фреймворк; не изобретай заново.

Какой RFC?

Где была введена каноническая формулировка «преждевременная оптимизация — корень всех зол», и какова ПОЛНАЯ цитата?

Викторина

Микробенчмарк запускает целевую функцию 500 итераций и репортирует среднее время на вызов. Метод на Java. Наиболее вероятный дефект этой установки?

Вспомните перед уходом
  1. 01
    Разбери четыре ловушки микробенчей в JIT-рантаймах и фикс для каждой.
  2. 02
    Изложи интеллектуальную родословную от Амдала 1967 до Google Wide Profiling 2010 в четырёх шагах.
Итог

Дисциплина «профиль первым» имеет 60-летнюю родословную. Амдал (1967) количественно обосновал потолок оптимизации. Кнут (1974) назвал дисциплину идентификации критических 3%. Густафсон (1988) скорректировал Амдала для масштабируемых нагрузок. GWP (2010) сделал непрерывное профилирование достаточно дешёвым для постоянного production-деплоя. Наивные микробенчи в JIT-рантаймах по умолчанию измеряют не то: JIT warmup означает, что работает интерпретатор, а не оптимизированный код; устранение мёртвого кода удаляет тело цикла; свёртка констант заменяет циклы литералами; масштабирование частоты CPU смещает измерения на ноутбуках. Промышленные фреймворки (JMH, criterion.rs, benchstat в Go) справляются со всем этим — используй их вместо самодельных циклов таймирования.

Связанные уроки
встречается в159
Продолжить восхождение ↑Hardware counters, профили холодного старта и безопасность профилей
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.