Výkonnostní testy
Benchmarkový nástroj vytváří stabilní JSON a čitelný report. Nachází se v bench/ v repozitáři storh a od 0.5.2 není součástí balíčku Composer, takže níže uvedené příkazy composer bench* lze spustit pouze z klonu, po provedení 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
Referenční výsledky
Mediány tří úplných běhů sady nástrojů, 50,000 záznamů na engine, na zařízení Apple M1 Pro (16 GB, macOS 26.5, APFS) s PHP 8.5 CLI; opcache a JIT jsou vypnuté:
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M
Každý zápis se před nahlášením uložení v storh flushne a synchronizuje pomocí fsync, takže rychlost zápisu omezuje souborový systém. Čtení probíhá z mezipaměti stránek operačního systému. Rychlosti jsou vypočtené z mediánů; vlastní hardware si ověřte spuštěním composer bench z klonu repozitáře.
| DocStore, 50k záznamů | medián |
|---|---|
put(), jeden trvalý soubor na záznam |
3.3k záznamů/s |
hromadné vložení putStream() |
3.7k záznamů/s |
importJsonl() |
3.6k záznamů/s |
bodové čtení get() s ověřením STAT |
5.6 µs |
| opětovné otevření existujícího úložiště | 0.26 ms |
indexovaný dotaz na rovnost, limit(100) |
1.1 ms |
indexované count() napříč kolekcí |
41 µs |
| sestavení indexu, 2 pole pro rovnost + 1 rozsah | 52k záznamů/s |
úplné stream() s opětovným ověřením STAT |
103k záznamů/s |
exportJsonl() |
104k záznamů/s |
| SegmentedLog, 50k záznamů, segmenty 16 KB | medián |
|---|---|
put(), fsync po každém připojení |
16k připojení/s |
hromadné vložení appendStream() |
56k záznamů/s |
| čtení kurzorem, 100 záznamů od poloviny | 2.1 ms |
| čtení v časovém rozsahu | 1.1 ms |
dotaz na rovnost count() |
11 µs |
compact() všech uzavřených segmentů |
69k záznamů/s |
| opětovné otevření s obnovou neúplného konce | 218k záznamů/s |
Benchmark uzavírá segment každých 16 KB, aby zatížil střídání segmentů; výchozí velikost segmentu je 1 MiB.
| Fronta, 50k úloh | medián |
|---|---|
enqueue(), fsync po každé události |
22k úloh/s |
claim() |
23k úloh/s |
complete() |
23k úloh/s |
enqueueMany() |
209k úloh/s |
claimMany() |
467k úloh/s |
completeMany() |
512k úloh/s |
| SQL Mirror, SQLite, 50k záznamů | medián |
|---|---|
počáteční push() |
61k řádků/s |
push() beze změn |
86k záznamů/s |
flush(), ID 100 |
7.1 ms |
indexované SQL COUNT nad zrcadlem |
6.9 ms |
rebuild() |
65k řádků/s |
obnova pull(), jeden trvalý soubor na záznam |
3.2k záznamů/s |
| Micro | medián |
|---|---|
cached get(), cold then warm (MemoryCache, STAT) |
83 µs / 6.3 µs |
| UUIDv7 generate | 1.3 µs |
| UUIDv7 validate | 0.30 µs |
| filtrování predikátů v paměti | 4.4M řádků/s |
Pokryté cíle:
- DocStore put, streamovací put, export/import JSONL, get, delete, stream a vytvoření indexu
- dotazy DocStore s indexem rovnosti, složeným indexem, rozsahovým indexem, seřazeným rozsahem vzestupně i sestupně, počtem podle indexu, hledáním podle ID i bez indexu
- SegmentedLog append, streamovací append, čtení pomocí kurzoru, čtení časového rozsahu, čtení zkomprimovaného časového rozsahu, hledání kurzorem a ID pomocí QueryBuilder, počty, počty hromadného přidání, statistiky, kompakce
- Queue enqueue, hromadné enqueue, claim, hromadné claim, complete, hromadné complete, requeue
- filtrování predikátů RecordQuery a QueryCondition nad řádky v paměti
- monotónní generování UUIDv7, generování s rozptylem časových razítek, validace a extrakce časových razítek
- obnova po poškozeném posledním řádku
- čtení z chladné a zahřáté mezipaměti s ověřením hashe, stavu nebo důvěryhodným ověřením
Výchozí výstupní cesta:
build/bench-current.json
Testy rozsahu zapisují jeden soubor JSON pro každou datovou sadu a engine do
build/bench-ranges/.
bench:gate porovnává dva soubory JSON s výsledky benchmarku a vrací nenulový kód, pokud je některá sledovaná metrika pomalejší než nakonfigurovaný limit. Pokud nejsou předány žádné hodnoty --metric, kontroluje všechny metriky obsažené v základním souboru.
bench:ci je kontrola regresí GitHub Actions. Spustí benchmark všech enginů s 1k záznamy, porovná vybrané metriky zápisu, čtení, mezipaměti a UUID se sledovanou základní hodnotou bench/baselines/ci-1k-all.json a používá velký výchozí limit, který omezuje běžné odchylky běhového prostředí a přitom zachytí výrazné zpomalení.
bench:jit spustí stejný testovací nástroj se zapnutou mezipamětí opcache a trasovacím JIT CLI a pro proces benchmarku vypne Xdebug, aby PHP mohl JIT skutečně zapnout.
Vygenerovaný soubor JSON obsahuje běhové příznaky, které ukazují, zda byly při daném běhu aktivní opcache a JIT.