性能测量方法论

用可复现的方法评估 Bun 的启动、吞吐与内存,不被微基准误导

最后更新于

为什么需要这页

“Bun 比 Node 快 N 倍”类的数字几乎全部来自 hello-world 微基准。真实收益取决于你的工作负载形状:启动敏感的 CLI、I/O 密集的 API、CPU 密集的转换,结论完全不同。本页给一套可复现的测量流程,让数字能进入决策文档。

原则

  1. 测自己的工作负载:真实路由、真实依赖、真实数据量;hello world 只测出框架开销。
  2. 一次只改一个变量:运行时、版本、代码、数据,只动一个,否则无法归因。
  3. 固定环境:同一台机器、同样的 CPU/内存限制、同样的环境变量;容器里测就用与生产一致的 limits。
  4. 看分布不看平均:至少报 p50/p95/p99;平均延迟会隐藏长尾。
  5. 先预热再采样:JIT 与连接池让前几百个请求没有代表性。

三个不同的测量目标

目标工具注意
启动时间(CLI、冷启动)hyperfine 'bun bench.ts' 'node bench.ts'两个运行时跑同一个文件,运行时才是唯一变量;hyperfine 默认报 mean±sd 与 min/max,需要分位数时用 --export-json 导出后处理
HTTP 吞吐与延迟oha / wrk客户端不能先成为瓶颈;连接数阶梯上升找拐点
内存与长稳/usr/bin/time -v、平台监控跑足够久观察 GC 与泄漏,不是启动峰值
# 示例:先跑一轮不计入结果的预热,再阶梯并发,每档 30 秒
oha -z 10s -c 50 http://localhost:3000/api/items > /dev/null  # 预热轮
oha -z 30s -c 50 http://localhost:3000/api/items
oha -z 30s -c 200 http://localhost:3000/api/items

Bun 相关的归因提醒

  • Bun 用 JavaScriptCore,Node/Deno 用 V8:某些代码形态(正则、特定内建、数字运算)在一个引擎上是快路径,在另一个上不是。差异是引擎特性,不一定是“Bun 快/慢”。
  • 安装与解析速度(bun install、冷启动 import)和请求期吞吐是两件事,不要混为一谈。
  • --watch--hot、开发模式都有可测量开销;一切数字以生产模式为准。
  • 容器内必须验证 CPU 限制下的行为:运行时线程池按可见核数伸缩,limits 设置不同结论就不同。

CI 中的性能回归

性能门禁不是跑满分,而是防退化:

  1. 选一个稳定、有代表性的场景(一个端点或一个脚本);
  2. 固定运行器规格与迭代次数,结果随构建产物存档;
  3. 阈值放宽到噪声之上(例如超过基线 20% 才失败),抖动大时先加样本而不是加容忍;
  4. 回归触发时按“一次一个变量”归因:版本、代码、依赖、环境。

报告格式(也适用于 Agent)

Workload: <endpoint/script + input shape>
Environment: <runtime + version, OS/arch, CPU/mem limits>
Command: <exact command>
Runs: <count, warmup>
Result: <p50/p95/p99, throughput, memory>
Conclusion: <what decision this number supports>

没有 WorkloadEnvironment 的性能数字不进入任何决策。

官方参考:ohahyperfineBun.serve 调优项