基准测试
基准测试框架会写入稳定的 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 条记录:
composer bench:repeat -- --dataset=50000 --engine=all --repeat=3 --memory-limit=512M
每次写入都会先刷新并执行 fsync,然后 storh 才会报告已存储,因此写入速率受文件系统限制。读取则使用操作系统页面缓存。速率根据中位数计算;你可以从仓库克隆版运行 composer bench,测试自己的硬件。
| DocStore,50k 条记录 | 中位数 |
|---|---|
put(),每条记录一个持久化文件 |
3.3k 条记录/s |
putStream() 批量导入 |
3.7k 条记录/s |
importJsonl() |
3.6k 条记录/s |
get() 点读取,经过 STAT 验证 |
5.6 µs |
| 重新打开现有存储 | 0.26 ms |
索引等值查询,limit(100) |
1.1 ms |
整个集合的索引 count() |
41 µs |
| 索引构建,2 个等值字段 + 1 个范围字段 | 52k 条记录/s |
完整 stream(),并进行 STAT 重新验证 |
103k 条记录/s |
exportJsonl() |
104k 条记录/s |
| SegmentedLog,50k 条记录,16 KB 分段 | 中位数 |
|---|---|
put(),每次追加都执行 fsync |
16k 次追加/s |
appendStream() 批量导入 |
56k 条记录/s |
| 游标从中点读取 100 条记录 | 2.1 ms |
| 时间范围读取 | 1.1 ms |
等值 count() |
11 µs |
对所有封存分段执行 compact() |
69k 条记录/s |
| 通过不完整尾部恢复后重新打开 | 218k 条记录/s |
基准测试每隔 16 KB 封存一个分段,以便对分段轮换进行压力测试;默认分段大小为 1 MiB。
| Queue,50k 个任务 | 中位数 |
|---|---|
enqueue(),每个事件都执行 fsync |
22k 个任务/s |
claim() |
23k 个任务/s |
complete() |
23k 个任务/s |
enqueueMany() |
209k 个任务/s |
claimMany() |
467k 个任务/s |
completeMany() |
512k 个任务/s |
| SQL Mirror,SQLite,50k 条记录 | 中位数 |
|---|---|
初始 push() |
61k 行/s |
未发生更改时的 push() |
86k 条记录/s |
flush(),100 个 ID |
7.1 ms |
对镜像执行索引 SQL COUNT |
6.9 ms |
rebuild() |
65k 行/s |
pull() 恢复,每条记录一个持久化文件 |
3.2k 条记录/s |
| 微基准 | 中位数 |
|---|---|
缓存的 get(),冷启动后转为热缓存(MemoryCache,STAT) |
83 µs / 6.3 µs |
| UUIDv7 生成 | 1.3 µs |
| UUIDv7 验证 | 0.30 µs |
| 内存谓词筛选 | 4.4M 行/s |
覆盖的目标:
- DocStore 写入、流式写入、JSONL 导出/导入、获取、删除、流式读取和索引构建
- 等值索引、复合索引、范围索引、升序和降序范围查询、索引计数、ID 查找以及非索引 DocStore 查询
- SegmentedLog 追加、流式追加、游标读取、时间范围读取、压缩后的时间范围读取、QueryBuilder 游标与 ID 查找、计数、批量追加计数、统计和压缩
- Queue 入队、批量入队、认领、批量认领、完成、批量完成、重新入队
- 对内存行执行 RecordQuery 和 QueryCondition 谓词筛选
- UUIDv7 单调生成、时间戳分布生成、验证和时间戳提取
- 末尾不完整行的恢复
- 使用哈希、stat 或可信验证执行冷缓存与热缓存读取
默认输出路径:
build/bench-current.json
范围测试会为每个数据集和引擎写入一个 JSON 文件,路径位于 build/bench-ranges/ 下。
bench:gate 会比较两个基准测试 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 是否处于启用状态。