ベンチマーク
ベンチマークハーネスは安定したJSONと、人が読めるレポートを書き出します。bench/にあり、storhリポジトリに含まれています。0.5.2以降、Composerパッケージには含まれていないため、以下の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
参考結果
Apple M1 Pro(16 GB、macOS 26.5、APFS)でPHP 8.5 CLI、opcacheとJITを無効にし、各エンジンで50,000レコードを処理したハーネスの全3回実行における中央値:
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M
すべての書き込みはstorhが保存済みと報告する前にフラッシュおよびfsyncされるため、書き込み速度はファイルシステムに制約されます。読み取りはOSのページキャッシュに対して行われます。速度は中央値から算出しています。ご自身のハードウェアで実行するには、リポジトリのクローンからcomposer benchを実行してください。
| DocStore、50kレコード | 中央値 |
|---|---|
put()、レコードごとに永続化ファイル1つ |
3.3k records/s |
putStream()一括取り込み |
3.7k records/s |
importJsonl() |
3.6k records/s |
get()ポイント読み取り、STAT検証済み |
5.6 µs |
| 既存ストアの再オープン | 0.26 ms |
インデックス付き等価検索、limit(100) |
1.1 ms |
コレクション全体のインデックス付きcount() |
41 µs |
| インデックス構築、等価フィールド2つ+範囲フィールド1つ | 52k records/s |
STATで再検証する完全なstream() |
103k records/s |
exportJsonl() |
104k records/s |
| SegmentedLog、50kレコード、16 KBセグメント | 中央値 |
|---|---|
put()、追記ごとにfsync |
16k appends/s |
appendStream()一括取り込み |
56k records/s |
| カーソル読み取り、中央から100レコード | 2.1 ms |
| 時間範囲読み取り | 1.1 ms |
等価count() |
11 µs |
封印済み全セグメントのcompact() |
69k records/s |
| 末尾不完全データの復旧を伴う再オープン | 218k records/s |
ベンチマークではセグメントの切り替えに負荷をかけるため、16 KBごとにセグメントを封印します。デフォルトのセグメントサイズは1 MiBです。
| キュー、50kジョブ | 中央値 |
|---|---|
enqueue()、イベントごとにfsync |
22k jobs/s |
claim() |
23k jobs/s |
complete() |
23k jobs/s |
enqueueMany() |
209k jobs/s |
claimMany() |
467k jobs/s |
completeMany() |
512k jobs/s |
| SQLミラー、SQLite、50kレコード | 中央値 |
|---|---|
初回push() |
61k rows/s |
変更なしでpush() |
86k records/s |
flush()、100 id |
7.1 ms |
ミラー上のインデックス付きSQLCOUNT |
6.9 ms |
rebuild() |
65k rows/s |
pull()復元、レコードごとに永続化ファイル1つ |
3.2k records/s |
| マイクロベンチマーク | 中央値 |
|---|---|
キャッシュ済みget()、コールドからウォームへ(MemoryCache、STAT) |
83 µs / 6.3 µs |
| UUIDv7の生成 | 1.3 µs |
| UUIDv7の検証 | 0.30 µs |
| メモリ内の述語フィルタリング | 4.4M rows/s |
対象範囲:
- DocStoreのput、ストリーミングput、JSONLエクスポート/インポート、get、delete、stream、インデックス構築
- 等価インデックス、複合インデックス、範囲インデックス、昇順・降順の範囲ソート、インデックス付き件数取得、ID検索、インデックスなしのDocStoreクエリ
- SegmentedLogのappend、ストリーミングappend、カーソル読み取り、時間範囲読み取り、圧縮済み時間範囲読み取り、QueryBuilderのカーソルとID検索、件数取得、一括appendの件数取得、統計、圧縮
- Queueのenqueue、一括enqueue、claim、一括claim、complete、一括complete、requeue
- メモリ内の行に対するRecordQueryとQueryConditionの述語フィルタリング
- UUIDv7の単調生成、タイムスタンプ分散生成、検証、タイムスタンプ抽出
- 末尾の不完全な行の復旧
- ハッシュ、stat、信頼済み検証を使ったコールド/ウォームキャッシュの読み取り
デフォルトの出力パス:
build/bench-current.json
範囲実行では、build/bench-ranges/の下にデータセットとエンジンごとにJSONファイルを1つ書き込みます。
bench:gateは2つのベンチマークJSONファイルを比較し、追跡対象の指標が設定されたしきい値より遅い場合はゼロ以外の終了コードを返します。--metricの値が指定されていない場合、ベースラインファイルにあるすべての指標を判定します。
bench:ciはGitHub Actionsの簡易回帰ゲートです。全エンジンで1kベンチマークを実行し、選択した書き込み/読み取り/キャッシュ/UUID指標を追跡対象のbench/baselines/ci-1k-all.jsonベースラインと比較します。ランナーで通常起きるばらつきを許容しつつ、大幅な性能低下は検出できるよう、デフォルトのしきい値は広めに設定されています。
bench:jitは同じハーネスをCLI opcacheとトレーシングJITを有効にして実行し、ベンチマークプロセスではXdebugを無効にしてPHPが実際にJITを有効化できるようにします。生成されるJSONには、その実行でopcacheとJITが有効だったかを示すランタイムフラグが含まれます。