booblik
DocsServices

booblik-benchmark

1. Зона ответственности

Числа. Модуль существует, чтобы каждое архитектурное утверждение этого проекта можно было опровергнуть замером, а не обсуждением.

Чем не занимается: не проверяет корректность (это тесты) и не входит в гейт — прогон идёт минутами, а гейт, которого никто не дожидается, гейтом быть перестаёт (решение Р5).

Главный инвариант: бенчмарк компилируется обычным assemble. Некомпилируемый бенчмарк тихо сгнивает при первом рефакторинге и обнаруживается ровно тогда, когда цифра нужна.

2. Контракт

Методика и текущие числа — benchmarking. Здесь только устройство модуля.

2а. Ключевые файлы (якоря кода)

ФайлЧто там
build.gradle.ktsконфигурации main (полная) и quick (порядок величины)
src/main/kotlin/.../benchmark/SegmentAppendBenchmark.ktзапись в сегмент: два режима × два размера × два режима сброса
src/main/kotlin/.../benchmark/MeasurementDir.ktгде измеряющему коду разрешено писать и отказ мерить память
src/main/kotlin/.../probe/LoadDriver.ktгенератор открытого цикла — один на обе нагрузочные пробы
src/main/kotlin/.../probe/RemoteLoadProbe.ktклиент без брокера: нагрузка с другой машины (M-38)

3. Как устроено

Бенчмарки лежат в main, а не в отдельном source set. Так их компилирует assemble — см. инвариант выше.

Две конфигурации, и они не взаимозаменяемы.

ЗадачаКомандаЧто даёт
Полный прогон./gradlew :booblik-benchmark:mainBenchmark5 прогревов + 10 итераций; цифру можно записывать
Порядок величины./gradlew :booblik-benchmark:mainQuickBenchmark2 + 3; в отчёт не годится — доверительный интервал шире самой оценки

allOpen на @State — обязательная строка, а не украшение. JMH генерирует наследника каждого @State-класса, а Kotlin делает классы финальными; без плагина генерация падает сообщением про финальный класс, которое читается как баг компилятора.

4. Зависимости

ТипИмяДля чего
Module:booblik-coreпредмет замера
Librarykotlinx-benchmark-runtimeаннотации и запуск поверх JMH
LibraryHdrHistogramперцентили задержки для нагрузочного стенда (M3)

5. Конфигурация

Параметры прогона — в build.gradle.kts, блок benchmark { configurations { … } }. Параметры самого замера — @Param в классе бенчмарка.

6. Инфраструктура и деплой

Нет.

7. Локальный запуск

./gradlew :booblik-benchmark:mainQuickBenchmark

Отчёт — booblik-benchmark/build/reports/benchmarks/.

8. Сознательные ограничения / грабли

  • Измеряющий код не имеет права писать в java.io.tmpdir. На Ubuntu 26.04 это tmpfs, то есть оперативная память: fsync там ничего не достигает и возвращается за время системного вызова. Проба M-24 в таком окружении отрапортовала fsync за 0,01 мс после 32 МиБ грязных данных — 3,2 ТБ/с, носителя с такой скоростью не существует — и напечатала свой вердикт про msync обычным уверенным тоном. Поймано в M-46, когда числа впервые снимались на арендованной машине. Отсюда MeasurementDir: каталог по умолчанию — build/measurements, то есть там же, где чекаут, а летучая файловая система — отказ, а не предупреждение. Предупреждение над числом читают после того, как числу уже поверили. Перенаправить прогон на конкретное устройство — -Dbooblik.bench.dir=/путь.
  • Носитель печатается в шапке каждой пробы (# storage: ext4 on /dev/sda1) по той же причине, по которой печатается версия JVM: число, у которого не записана среда, потом не с чем сравнить.
  • Бенчмарк-метод не может возвращать value class. Kotlin манглит имя, генератор JMH отвергает его как невалидный Java-идентификатор (ресёрч §1.8). Отсюда Long вместо Offset в сигнатуре append(). Возвращать что-то метод обязан — иначе JMH выкинет вызов как мёртвый код.
  • «RPS» в этом проекте — две разные величины, и путать их нельзя. Бенчмарки JMH меряют пропускную способность слоя хранения, нагрузочные пробы — запросы по сети. Числа из них несравнимы между собой по определению.
  • Нагрузочных проб две, и генератор у них общий (LoadDriver). probeLoad поднимает брокер в своём же процессе, probeRemoteLoad только подключается к чужому. Общий драйвер здесь не экономия строк: будь у распределённого прогона своя копия цикла, у каждой найденной им разницы было бы два объяснения — сеть и вторая реализация, — и второе не опровергнуть, не вычитав обе.
  • probeRemoteLoad не меряет процессор брокера и не должен. Брокер — другой процесс на другой машине; когда упирается провод, единственная оставшаяся ось — процессор на отданный байт, и читать его надо из /proc того хоста, а не из этой JVM (M-38).
  • Сравнивать прогоны с разных хостов нельзя. Файловая система, writeback и сам JDK влияют больше, чем разница, которую мы обычно ищем. Правило и обязательная шапка отчёта — в benchmarking.

On this page