
# Benchmarks

Het benchmarkharnas schrijft stabiele JSON en een leesbaar rapport. Het staat
in `bench/` in de repository [storh](https://github.com/inline0/storh) en maakt
sinds 0.5.2 geen deel meer uit van het pakket Composer, dus de onderstaande `composer bench*`-
commando's voert u alleen uit vanuit een kloon, na `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
```

## Referentieresultaten

Medianen van drie volledige runs van het harnas, 50,000 records per engine, op een
Apple M1 Pro (16 GB, macOS 26.5, APFS) met PHP 8.5 CLI, opcache en JIT uitgeschakeld:

```bash
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M
```

Elke schrijfbewerking wordt geflusht en gefsynct voordat storh meldt dat deze is opgeslagen, dus de schrijfsnelheid wordt begrensd door het bestandssysteem. Leesbewerkingen gebruiken de OS-paginacache. Snelheden zijn afgeleid van de medianen; voer `composer bench` uit vanuit een kloon van de repository voor uw eigen hardware.

| DocStore, 50k records                          | mediaan          |
| ---------------------------------------------- | --------------- |
| `put()`, één duurzaam bestand per record       | 3.3k records/s  |
| `putStream()` bulkopname                              | 3.7k records/s  |
| `importJsonl()`                                         | 3.6k records/s  |
| `get()` puntlezing, STAT-gevalideerd             | 5.6 µs          |
| bestaande store opnieuw openen                 | 0.26 ms         |
| geïndexeerde gelijkheidsquery, `limit(100)`         | 1.1 ms          |
| geïndexeerde `count()` door de hele collectie     | 41 µs           |
| index opbouwen, 2 gelijkheidsvelden + 1 bereikveld | 52k records/s   |
| volledige `stream()` met STAT-herbevestiging         | 103k records/s  |
| `exportJsonl()`                                         | 104k records/s  |

| SegmentedLog, 50k records, segmenten van 16 KB | mediaan          |
| ---------------------------------------------- | --------------- |
| `put()`, fsync per toevoeging                  | 16k toevoegingen/s |
| `appendStream()` bulkopname                              | 56k records/s   |
| cursorlezing, 100 records vanaf het midden  | 2.1 ms          |
| lezing over een tijdsbereik                    | 1.1 ms          |
| gelijkheids`count()`                              | 11 µs           |
| `compact()` alle verzegelde segmenten                | 69k records/s   |
| opnieuw openen met herstel van onvolledige staart | 218k records/s  |

De benchmark verzegelt elke 16 KB een segment om het wisselen van segmenten te belasten; de
defaultsegmentgrootte is 1 MiB.

| Queue, 50k taken                              | mediaan          |
| ---------------------------------------------- | --------------- |
| `enqueue()`, fsync per gebeurtenis                | 22k taken/s     |
| `claim()`                                       | 23k taken/s     |
| `complete()`                                       | 23k taken/s     |
| `enqueueMany()`                                       | 209k taken/s    |
| `claimMany()`                                       | 467k taken/s    |
| `completeMany()`                                       | 512k taken/s    |

| SQL Mirror, SQLite, 50k records              | mediaan          |
| ---------------------------------------------- | --------------- |
| initiële `push()`                                | 61k rijen/s     |
| `push()` zonder wijzigingen                     | 86k records/s   |
| `flush()`, 100 ids                             | 7.1 ms          |
| geïndexeerde SQL `COUNT` over de mirror       | 6.9 ms          |
| `rebuild()`                                         | 65k rijen/s     |
| `pull()` herstellen, één duurzaam bestand per record | 3.2k records/s  |

| Micro                                          | mediaan          |
| ---------------------------------------------- | --------------- |
| gecachte `get()`, koud dan warm (MemoryCache, STAT) | 83 µs / 6.3 µs |
| UUIDv7 genereren                               | 1.3 µs          |
| UUIDv7 valideren                               | 0.30 µs         |
| predicatefiltering in het geheugen             | 4.4M rijen/s    |

Gedekte doelen:

- DocStore put, streaming put, JSONL export/import, get, delete, stream en indexopbouw
- geïndexeerde gelijkheids-, samengestelde index- en bereikquery's, oplopende en aflopende geordende bereiken, geïndexeerde telling, ID-lookup en niet-geïndexeerde DocStore-query's
- SegmentedLog append, streaming append, cursorlezingen, lezingen over tijdsbereiken, lezingen over gecompacteerde tijdsbereiken, QueryBuilder-cursor- en ID-lookups, tellingen, tellingen van bulk-appends, statistieken en compactie
- Queue enqueue, bulk enqueue, claim, bulk claim, complete, bulk complete en requeue
- predicatefiltering met RecordQuery en QueryCondition over rijen in het geheugen
- UUIDv7 monotone generatie, generatie met gespreide tijdstempels, validatie en extractie van tijdstempels
- herstel van onvolledige afsluitende regels
- koude en warme cachelezingen met hash-, stat- of vertrouwde validatie

Standaarduitvoerpad:

```text
build/bench-current.json
```

Bereikruns schrijven één JSON-bestand per dataset en engine onder
`build/bench-ranges/`.

`bench:gate` vergelijkt twee benchmark-JSON-bestanden en eindigt met een niet-nulstatus als een gevolgde metriek trager is dan de ingestelde drempel. Als er geen `--metric`-waarden worden opgegeven, controleert het alle metrieken in het baselinebestand.

`bench:ci` is de GitHub Actions rookregressietest. Deze voert een 1k-benchmark voor alle engines uit, vergelijkt geselecteerde schrijf-, lees-, cache- en UUID-statistieken met de bijgehouden `bench/baselines/ci-1k-all.json`-baseline en gebruikt een ruime standaarddrempel om normale runner-ruis te vermijden en toch grote prestatieregressies te detecteren.

`bench:jit` voert hetzelfde harnas uit met CLI-opcache en tracing JIT ingeschakeld,
en schakelt Xdebug uit voor het benchmarkproces zodat PHP JIT daadwerkelijk kan inschakelen.
De gegenereerde JSON bevat runtimevlaggen die aangeven of opcache en JIT actief waren tijdens die run.
