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:mainBenchmark | 5 прогревов + 10 итераций; цифру можно записывать |
| Порядок величины | ./gradlew :booblik-benchmark:mainQuickBenchmark | 2 + 3; в отчёт не годится — доверительный интервал шире самой оценки |
allOpen на @State — обязательная строка, а не украшение. JMH генерирует наследника
каждого @State-класса, а Kotlin делает классы финальными; без плагина генерация падает
сообщением про финальный класс, которое читается как баг компилятора.
4. Зависимости
| Тип | Имя | Для чего |
|---|---|---|
| Module | :booblik-core | предмет замера |
| Library | kotlinx-benchmark-runtime | аннотации и запуск поверх JMH |
| Library | HdrHistogram | перцентили задержки для нагрузочного стенда (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.