
# Tolok Ukur

Harness benchmark menulis JSON yang stabil dan laporan yang mudah dibaca. Lokasinya
ada di `bench/` dalam repositori [storh](https://github.com/inline0/storh) dan tidak
lagi menjadi bagian dari paket Composer sejak 0.5.2, jadi perintah `composer bench*`
di bawah hanya dapat dijalankan dari hasil clone, setelah `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
```

## Hasil referensi

Median dari tiga proses harness penuh, 50,000 record per mesin, pada
Apple M1 Pro (16 GB, macOS 26.5, APFS) dengan PHP 8.5 CLI, opcache dan JIT dinonaktifkan:

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

Setiap penulisan di-flush dan di-fsync sebelum storh melaporkan bahwa data telah tersimpan, sehingga laju penulisan dibatasi oleh sistem file. Pembacaan dilakukan terhadap cache halaman OS. Laju dihitung dari median; jalankan `composer bench` dari hasil clone repositori untuk mengukur perangkat keras Anda sendiri.

| DocStore, 50k record                          | median          |
| ---------------------------------------------- | --------------- |
| `put()`, satu file persisten per record           | 3.3k record/s  |
| `putStream()` ingest massal                      | 3.7k record/s  |
| `importJsonl()`                                | 3.6k record/s  |
| `get()` pembacaan titik, divalidasi STAT             | 5.6 µs          |
| membuka kembali store yang ada                       | 0.26 ms         |
| kueri kesetaraan berindeks, `limit(100)`           | 1.1 ms          |
| `count()` berindeks di seluruh koleksi        | 41 µs           |
| pembangunan indeks, 2 field kesetaraan + 1 field rentang        | 52k record/s   |
| `stream()` penuh dengan validasi ulang STAT        | 103k record/s  |
| `exportJsonl()`                                | 104k record/s  |

| SegmentedLog, 50k record, segmen 16 KB      | median          |
| ---------------------------------------------- | --------------- |
| `put()`, fsync per append                      | 16k append/s   |
| `appendStream()` ingest massal                   | 56k record/s   |
| pembacaan kursor, 100 record dari titik tengah     | 2.1 ms          |
| pembacaan rentang waktu                                | 1.1 ms          |
| `count()` kesetaraan                             | 11 µs           |
| `compact()` semua segmen tersegel                | 69k record/s   |
| membuka ulang dengan pemulihan akhir terpotong                 | 218k record/s  |

Benchmark menyegel segmen setiap 16 KB untuk menguji pergantian segmen; ukuran segmen
bawaan adalah 1 MiB.

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

| SQL Mirror, SQLite, 50k record                | median          |
| ---------------------------------------------- | --------------- |
| `push()` awal                               | 61k baris/s      |
| `push()` tanpa perubahan                  | 86k record/s   |
| `flush()`, 100 id                             | 7.1 ms          |
| SQL berindeks `COUNT` di seluruh mirror            | 6.9 ms          |
| `rebuild()`                                    | 65k baris/s      |
| restore `pull()`, satu file persisten per record  | 3.2k record/s  |

| Mikro                                          | median          |
| ---------------------------------------------- | --------------- |
| `get()` yang di-cache, cold lalu warm (MemoryCache, STAT) | 83 µs / 6.3 µs |
| generasi UUIDv7                                | 1.3 µs          |
| validasi UUIDv7                                | 0.30 µs         |
| penyaringan predikat dalam memori                  | 4.4M baris/s     |

Target yang dicakup:

- DocStore put, streaming put, ekspor/impor JSONL, get, delete, stream, dan pembangunan indeks
- kueri DocStore dengan indeks kesetaraan, indeks gabungan, indeks rentang, rentang berurutan naik dan turun, hitung terindeks, pencarian ID, dan tanpa indeks
- SegmentedLog append, streaming append, pembacaan kursor, pembacaan rentang waktu, pembacaan rentang waktu hasil pemadatan, pencarian kursor dan ID QueryBuilder, hitung, hitung append massal, statistik, pemadatan
- Queue enqueue, enqueue massal, claim, claim massal, complete, complete massal, requeue
- penyaringan predikat RecordQuery dan QueryCondition pada baris dalam memori
- generasi monotonik UUIDv7, generasi sebaran timestamp, validasi, dan ekstraksi timestamp
- pemulihan baris akhir yang terpotong
- pembacaan cache cold dan warm dengan validasi hash, stat, atau tepercaya

Jalur output bawaan:

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

Proses rentang menulis satu file JSON per dataset dan mesin di bawah
`build/bench-ranges/`.

`bench:gate` membandingkan dua file benchmark JSON dan keluar dengan status non-zero jika metrik yang dipantau mana pun lebih lambat daripada ambang yang dikonfigurasi. Jika tidak ada nilai `--metric` yang diberikan, semua metrik dalam file baseline akan menjadi pengujian batas.

`bench:ci` adalah gerbang regresi smoke GitHub Actions. Ini menjalankan benchmark 1k untuk semua mesin, membandingkan metrik tulis/baca/cache/UUID yang dipilih dengan baseline `bench/baselines/ci-1k-all.json` yang dipantau, dan menggunakan ambang bawaan yang lebar untuk menghindari variasi normal runner sambil tetap menggagalkan regresi kinerja yang besar.

`bench:jit` menjalankan harness yang sama dengan opcache CLI dan tracing JIT diaktifkan,
serta menonaktifkan Xdebug untuk proses benchmark agar PHP benar-benar dapat mengaktifkan JIT.
JSON yang dihasilkan menyertakan flag runtime yang menunjukkan apakah opcache dan JIT aktif pada proses tersebut.
