Бенчмаркове
Инструментът за сравнителни тестове записва стабилни JSON и четим за хората отчет. Намира се в bench/ в хранилището storh и не е част от пакета Composer от 0.5.2 насам, затова командите composer bench* по-долу се изпълняват само от клонирано хранилище след composer install:
composer bench
composer bench -- --dataset=100000 --engine=doc
composer bench -- --dataset=100000 --engine=log
composer bench -- --dataset=100000 --engine=queue
composer bench -- --dataset=100000 --engine=cache --cache-validation=trust
composer bench -- --dataset=100000 --engine=filter
composer bench -- --dataset=1000000 --engine=uuid
composer bench:jit -- --dataset=1000000 --engine=uuid
composer bench:repeat -- --dataset=100000 --engine=doc --repeat=5
composer bench:repeat -- --dataset=1000000 --engine=filter --repeat=5 --memory-limit=512M
composer bench:range -- --datasets=1000,10000,50000,100000
composer bench:range -- --datasets=100000 --engines=cache --memory-limit=512M
composer bench:compare build/bench-main.json build/bench-current.json
composer bench:gate -- build/bench-main.json build/bench-current.json --threshold=10 --metric=doc.put --metric=log.stream
composer bench:ci
Референтни резултати
Медиани от три пълни изпълнения на инструмента, по 50,000 записа за всеки механизъм, на Apple M1 Pro (16 GB, macOS 26.5, APFS) с PHP 8.5 CLI, при изключени opcache и JIT:
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M
Всеки запис се flush-ва и fsync-ва, преди storh да отчете, че е съхранен, затова скоростта на запис зависи от файловата система. Четенията използват кеша на страниците на операционната система. Скоростите са изчислени от медианите; изпълнете composer bench от клонирано хранилище на проекта, за да измерите на собствения си хардуер.
| DocStore, 50k записа | медиана |
|---|---|
put(), един траен файл за всеки запис |
3.3k записа/сек |
putStream(), групово зареждане |
3.7k записа/сек |
importJsonl() |
3.6k записа/сек |
get(), четене на запис, проверен чрез STAT |
5.6 µs |
| повторно отваряне на съществуващо хранилище | 0.26 ms |
индексирана заявка за равенство, limit(100) |
1.1 ms |
индексирано count() в цялата колекция |
41 µs |
| изграждане на индекс, 2 полета за равенство + 1 за диапазон | 52k записа/сек |
пълно stream() с повторна проверка чрез STAT |
103k записа/сек |
exportJsonl() |
104k записа/сек |
| SegmentedLog, 50k записа, сегменти от 16 KB | медиана |
|---|---|
put(), fsync при всяко добавяне |
16k добавяния/сек |
appendStream(), групово зареждане |
56k записа/сек |
| четене с курсор, 100 записа от средата | 2.1 ms |
| четене на времеви диапазон | 1.1 ms |
count() за равенство |
11 µs |
compact() на всички запечатани сегменти |
69k записа/сек |
| повторно отваряне с възстановяване на прекъснатия край | 218k записа/сек |
При сравнителния тест сегментът се запечатва на всеки 16 KB, за да се натоварят смяните на сегменти; размерът по подразбиране е 1 MiB.
| Queue, 50k задачи | медиана |
|---|---|
enqueue(), fsync при всяко събитие |
22k задачи/сек |
claim() |
23k задачи/сек |
complete() |
23k задачи/сек |
enqueueMany() |
209k задачи/сек |
claimMany() |
467k задачи/сек |
completeMany() |
512k задачи/сек |
| SQL Mirror, SQLite, 50k записа | медиана |
|---|---|
първоначално push() |
61k реда/сек |
push() без промени |
86k записа/сек |
flush(), 100 идентификатора |
7.1 ms |
индексирано SQL COUNT върху огледалото |
6.9 ms |
rebuild() |
65k реда/сек |
възстановяване на pull(), един траен файл за всеки запис |
3.2k записа/сек |
| Micro | медиана |
|---|---|
кеширан get(), студен, после топъл (MemoryCache, STAT) |
83 µs / 6.3 µs |
| UUIDv7 генериране | 1.3 µs |
| UUIDv7 валидиране | 0.30 µs |
| филтриране в паметта по предикат | 4.4M реда/s |
Обхванати цели:
- DocStore put, streaming put, JSONL export/import, get, delete, stream и изграждане на индекс
- заявки към DocStore с индекси за равенство, съставни индекси, индекси за диапазони, сортирани диапазони във възходящ и низходящ ред, преброяване по индекс, търсене по ID и без индекси
- SegmentedLog append, streaming append, четене с курсор, четене по времеви диапазон, четене по компактиран времеви диапазон, търсения с курсор и по ID чрез QueryBuilder, преброявания, преброявания при групово добавяне, статистика, компактиране
- Queue enqueue, bulk enqueue, claim, bulk claim, complete, bulk complete, requeue
- филтриране по предикат с RecordQuery и QueryCondition върху редове в паметта
- UUIDv7 монотонно генериране, генериране с разпределение на времевите отметки, валидиране и извличане на времеви отметки
- възстановяване след прекъснат последен ред
- четене от кеш при студен и топъл кеш с хеш, проверка със stat или доверено валидиране
Път по подразбиране за изход:
build/bench-current.json
Изпълненията с диапазони записват по един файл JSON за всеки набор от данни и машина в
build/bench-ranges/.
bench:gate сравнява два файла JSON с резултати от бенчмаркове и завършва с ненулев код, ако която и да е проследявана метрика е по-бавна от зададения праг. Ако не са подадени стойности --metric, проверява всички метрики във файла с базовите резултати.
bench:ci е проверката за регресии при тестване на дим с GitHub Actions. Тя изпълнява бенчмарк с 1k записа за всички машини, сравнява избрани метрики за запис/четене/кеширане/UUID с проследяваните базови резултати bench/baselines/ci-1k-all.json и използва широк праг по подразбиране, за да избегне обичайните отклонения при изпълнение, като същевременно засича сериозни регресии в производителността.
bench:jit изпълнява същия тестов набор с активирани CLI opcache и tracing JIT и изключва Xdebug за процеса на бенчмарка, за да може PHP действително да активира JIT.
Генерираният JSON включва флагове на средата за изпълнение, които показват дали opcache и JIT са били активни при това изпълнение.