
# Výkonnostní testy

Benchmarkový nástroj vytváří stabilní JSON a čitelný report. Nachází se v `bench/` v repozitáři [storh](https://github.com/inline0/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`:

```bash
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é:

```bash
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:

```text
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.
