Bun 1.4 到底快了多少,我跑了一轮性能测试
和 1.3.12 相比,Bun 1.4.0 的启动速度提高了 1.41 到 1.85 倍,进程内存少了 45% 到 56%。new URL() 快了 3.1 到 5.4 倍,hex 解码快了约 4.5 倍,isbot 更是快了 161 到 209 倍。
hello.js 启动中位数Bun 1.4 相对 1.3.12 提升了多少
启动、进程内存、URL、hex 解码和 isbot 在两组测试中都明显变快。下面每个单元格都按 1.3.12 到 1.4.0 的顺序列出,最右边直接写提升幅度。
| 项目 | Mac 耗时或大小 | Mac 提升 | Linux 耗时或大小 | Linux 提升 |
|---|---|---|---|---|
hello.js 启动 | 10.67 → 7.59 ms | 快 1.41× | 13.29 → 7.20 ms | 快 1.85× |
hello.js 最小 RSS | 20.9 → 10.7 MB | 降低 49% | 28.5 → 12.4 MB | 降低 56% |
空 Bun.serve RSS | 23.4 → 12.9 MB | 降低 45% | 31.4 → 15.6 MB | 降低 50% |
new URL(abs) | 337 → 63 ns | 快 5.4× | 1143 → 370 ns | 快 3.1× |
new URL(rel, base) | 576 → 128 ns | 快 4.5× | 1303 → 673 ns | 快 1.94× |
Buffer.from(hex) 1 MiB | 2.27 → 0.49 ms | 快 4.6× | 2.21 → 0.49 ms | 快 4.5× |
| RegExp 匹配邮箱 | 14.2 → 12.9 ns | 快 1.10× | 335 → 93 ns | 快 3.6× |
JSON.parse 千项数组 | 14.4 → 11.3 µs | 快 1.28× | 46.7 → 25.5 µs | 快 1.83× |
isbot() | 192225 → 918 ns | 快 209× | 375581 → 2339 ns | 快 161× |
gzipSync 约 1 MB | 1.32 → 0.38 ms | 快 3.5× | 4.09 → 0.64 ms | 快 6.4× |
gunzipSync 约 1 MB | 0.48 → 0.38 ms | 快 1.3× | 1.18 → 1.31 ms | 慢约 11% |
| Bun 二进制 | 59 → 61 MB | 增加 2 MB | 96 → 78 MB | 缩小 19% |
这张表里最值得信的是启动和 RSS。它们测的是完整进程,在两种系统上也给出了接近的幅度。RSS 取五次运行中的最小峰值,代表刚启动时的内存占用,不能代替服务运行几天后的内存曲线。
gzipSync 用的是高度重复的字母串,所以 3.5 到 6.4 倍只属于这段输入。Linux 上的 gunzipSync 还慢了约 11%。这些结果说明 Bun 1.4 优化了不少热点,但没有让每一种工作都变快。
Node 只作为参照。hello.js 在 Node 22 和 Node 20 上分别需要 24.24 ms、35.51 ms,最小 RSS 分别是 38.2 MB、38.0 MB。Bun 1.4 在这两项上仍然明显更轻。
Mac M2 Max,12 核,96 GB。启动跑 30 次取中位数。RSS 跑 5 次取最小峰值。
Linux Ubuntu 24.04.4,x86_64,1 核 Xeon,961 MB。启动跑 20 次,RSS 同样跑 5 次。
版本 Bun 1.4.0 34cbb9a40,Bun 1.3.12 700fc117a。两个版本都放在临时目录,没有替换机器原来的 Bun。
几个微基准还要拆开看
isbot 快了一百多倍
这项测试沿用了发布博客里的 8 条 UA 和 isbot 5.2.1。Mac 上一次调用从约 192 微秒降到 0.918 微秒,算下来是 209 倍。Linux 从约 376 微秒降到 2.34 微秒,约 161 倍。
1.3.12 按官方的完整循环会跑几分钟,我缩小了旧版本的循环次数。因此这项测试更适合看数量级。它的绝对值和官方给出的 218 微秒、1.07 微秒很接近,百倍级改善有足够的支撑。
base64url 会挑输入
这组测试每次解码 1 MiB 数据,解码结果会被读取,每组跑五轮取中位数。我分别用了混合字节和重复字节,两种输入给出了完全不同的结果。
| 1 MiB 输入 | Bun 1.4.0 | Bun 1.3.12 | 1.4 相对 1.3 |
|---|---|---|---|
| Mac 混合字节 | 145 µs | 3771 µs | 约 26× |
| Mac 重复字节 | 141 µs | 107 µs | 1.4 耗时多约 32% |
| Linux 混合字节 | 220 µs | 4523 µs | 约 20.5× |
| Linux 重复字节 | 222 µs | 243 µs | 约 1.10× |
同一个 API,输入内容一换,Bun 1.4 相对 1.3.12 的表现就从慢 32% 变成快 26 倍。Bun 发布文章里的 46 倍来自它所用的负载和机器,不能直接套到所有 base64url 数据上。项目里大量处理 token、JWT 或图片片段时,最好拿自己的数据再测一次。
Promise 有提升,幅度看操作
Promise 这组循环 200 万次。Bun 1.4.0 的 resolved await 在 Mac 上比 1.3.12 快 1.58 倍,在 Linux 上快 1.68 倍。四层 .then() 链在 Mac 上快 1.94 倍,在 Linux 上快 1.99 倍。
| Promise 操作 | Mac 1.4 对 1.3 | Linux 1.4 对 1.3 |
|---|---|---|
| await resolved promise | 1.58× | 1.68× |
| 四层 then 链 | 1.94× | 1.99× |
| Promise.all 四项 | 1.29× | 1.37× |
| Promise.race 四项 | 1.02× | 1.18× |
发布博客写的是 1.5 到 2.4 倍。这个范围能覆盖 await 和 then 链,覆盖不了所有 Promise 操作。对应用来说,最后还得看自己的异步路径由哪一种操作占大头。
JSON 和并行测试
解析一个 1000 项数组时,Bun 1.4 在 Mac 上快了 1.28 倍,在 Linux 上快了 1.83 倍。Node 22 在 Mac 上是 9.1 微秒,Node 20 在 Linux 上是 22.5 微秒,仍然比对应平台的 Bun 1.4 快。
三个测试文件各等待 50 ms,Mac 上串行需要 156 ms,使用 bun test --parallel 后是 73 ms。Linux 的单核 VPS 从 167 ms 降到 123 ms。这个数字只说明并行运行测试文件能省多少时间,没有用于比较 1.4.0 和 1.3.12。
功能变化里有几样确实省依赖
Bun.Image 在 1.3.12 里还不存在。我用 64 × 64 PNG 跑过读取、缩放和 JPEG 编码,输出文件头正确。第一次看到 width 和 height 都是 -1,我确实把它当成 bug。调用 metadata() 完成解码以后,尺寸才会出现。
Bun.WebView 在 Mac 上走系统 WebKit。一个内联 HTML 页面可以取标题和截图。Linux 机器没有 Chrome,启动时会明确报缺少浏览器。那台 VPS 只有 1 GB 内存,我没有为了一个 smoke test 再塞 Chromium。
目录静态资源、Range 206、Bun.Terminal、Hono 4.13.3 和内置 SQLite 接口都跑通了。这里证明的是接口可用,不能把一次成功启动写成兼容性认证。Playwright、Next.js 16、vitest coverage、dd-trace 和长期泄漏都没有进入这次测试。
包管理器的变化更容易在日常工作里碰到。新 lockfile 默认是 v2,bun audit fix、bun dedupe、bun prune、bun pm licenses 和 bun pm diff 已经能用。官方宣传的 7 倍暖安装需要大型 isolated workspace,这次只有几个小包,我没有拿它凑数字。
升级前我会先查这几处
| 变化 | 实际影响 |
|---|---|
| modules 137 → 147 | 原生 addon 要有新的预编译包,缺少时需要重编。sharp、better-sqlite3 和 node-pty 值得先查。 |
| lockfileVersion 2 | 旧 Bun 读不了新 lockfile。CI、本地和部署环境要一起升级,仓库里的 lockfile 解析工具也要跟着看。 |
| node 启动方式 | Bun 以 node 这个名字运行时,不再自动加载 .env。直接执行 bun file.js 仍会加载。 |
| 解析器与全局对象 | YAML 里的 yes、on、off 现在是字符串。TOML 更严格,Temporal 默认存在,XML import 会返回文档对象。 |
| 旧接口收紧 | res.writeHeader() 已删除,递归删除要用 fs.rm。读取 body 以后再调用 Response.clone() 会抛错。 |
我会升,但会把版本一起升
日常脚本和新项目可以直接试 1.4。启动更快,十几 MB 的小进程少掉一半 RSS,开很多短命进程时很实在。pm diff 和内置图片处理也能少装几个包。
现有项目需要多走一步。我会先看 native addon,再让开发机、CI 和部署环境使用同一个 Bun 版本。lockfile v2 一旦进仓库,继续混用 1.3 会把问题留给下一个人。
长驻服务会先进 staging。1.4.0 是 Rust 重写后的第一个稳定版本,短测只能说明它能启动、能跑这些路径,说明不了几天后的内存曲线。Rust 重写有没有把旧问题真正解决,要靠更长的生产数据回答。