Bun 多线程有多快?postMessage 传输字符串比 Node.js 快 400 倍!
Bun v1.2.21 为
postMessage引入了字符串快速路径------传输 3MB 字符串仅需 593 纳秒,比 Node.js 快约 408 倍 ,内存占用减少 22 倍。本文带你全面了解 Bun Worker API 的使用方式与性能优化原理。
一、Worker 基础
Bun 实现了浏览器标准的 Web Workers API,让你在独立线程中运行 JavaScript,同时与主线程共享 I/O 资源。
与 Node.js 不同,Bun 的 Worker 开箱支持 TypeScript、JSX、ES Modules 和 CommonJS,无需任何构建工具。
创建 Worker
主线程:
ts
const worker = new Worker("./worker.ts");
worker.postMessage("hello");
worker.onmessage = (event) => {
console.log(event.data); // 'world'
};
Worker 线程(worker.ts):
ts
declare var self: Worker;
self.onmessage = (event: MessageEvent) => {
console.log(event.data); // 'hello'
postMessage("world");
};
declare var self: Worker;用于消除 TypeScript 类型报错。
预加载模块(preload)
在 Worker 启动前加载监控、追踪等初始化代码:
ts
const worker = new Worker("./worker.ts", {
preload: ["./sentry.js", "./opentelemetry.js"],
});
blob: URL
从字符串或动态代码创建 Worker:
ts
const blob = new Blob(
[`self.onmessage = (e) => postMessage(e.data);`],
{ type: "application/typescript" }
);
const worker = new Worker(URL.createObjectURL(blob));
"open" 事件
Worker 就绪后触发(浏览器无此事件):
ts
worker.addEventListener("open", () => {
console.log("worker is ready");
});
消息会自动排队直到 Worker 就绪,无需等待 "open" 事件即可发送。
二、性能优化:为什么快 400 倍?
官方基准测试
Bun 官方在 GitHub 提供了测试数据:
| 字符串大小 | Bun 1.2.21 | Bun 1.2.20 | Node.js 24.6.0 |
|---|---|---|---|
| 11 字符 | 543 ns | 598 ns | 806 ns |
| 14 KB | 460 ns | 1,350 ns | 1,220 ns |
| 3 MB | 593 ns | 326,290 ns | 242,110 ns |
关键结论:
- 传输 3MB 字符串:Bun 仅需 593 ns ,Node.js 需 242,110 ns ------快约 408 倍。
- 字符串大小对 Bun 几乎无影响------11 字符和 3MB 耗时相近(543 ns vs 593 ns)。
技术原理
传统 postMessage 使用结构化克隆算法序列化数据------复制整个字符串到新缓冲区,再在另一端反序列化。
Bun 的核心洞察 :JavaScriptCore 引擎中的字符串是线程安全的引用计数对象:
cpp
class StringImplShape {
std::atomic m_refCount; // 线程安全
unsigned m_length; // 不可变
union {
const LChar* m_data8;
const char16_t* m_data16;
};
mutable unsigned m_hashAndFlags;
};
既然字符串本身线程安全,为何要复制?直接传递指针即可。
快速路径实现:
cpp
WTF::String toCrossThreadShareable(WTF::String& string) {
auto* impl = string.impl();
// 三类字符串必须序列化:Atom、Symbol、子字符串
if (impl->isAtom() || impl->isSymbol() ||
impl->bufferOwnership() == StringImpl::BufferSubstring)
return string.isolatedCopy();
impl->hash();
impl->setNeverAtomicize();
return string; // 零拷贝!直接共享指针
}
生效条件(全部满足时自动触发):
- 使用
postMessage或structuredClone - 只发送字符串(不混发其他数据)
- 字符串不是 Atom、Symbol 或子字符串
- 发送到同一进程内的另一个线程
- 字符串长度 ≥ 256 字符
简单对象快速路径
对于只包含基本类型(字符串、数字、布尔、null、undefined)的简单对象,Bun 同样有优化路径,性能提升 2-241 倍。
Bun(优化后):
ts
postMessage({ prop: "11 chars", ...9 more props }) - 648ns
postMessage({ prop: "14 KB string", ...9 more props }) - 719ns
postMessage({ prop: "3 MB string", ...9 more props }) - 1.26µs
Node.js v24.6.0(对比):
js
postMessage({ prop: "11 chars", ...9 more props }) - 1.19µs
postMessage({ prop: "14 KB string", ...9 more props }) - 2.69µs
postMessage({ prop: "3 MB string", ...9 more props }) - 304µs
简单对象快速路径激活条件:
- 纯对象(无原型链修改)
- 仅包含可枚举、可配置的数据属性
- 无非索引属性、getter/setter
- 所有属性值都是基本类型或字符串
三、高级特性
生命周期管理(ref / unref)
默认 Worker 会阻止主进程退出。unref() 可解耦生命周期:
ts
const worker = new Worker("./worker.ts");
worker.unref(); // 主进程可独立退出
// 之后重新关联
worker.ref();
创建时也可设置:
ts
new Worker("./worker.ts", { ref: false });
终止 Worker
自动终止:事件循环空闲时退出。强制终止:
ts
worker.terminate();
Worker 内部可调用 process.exit(code) 自行退出,主线程收到 "close" 事件:
ts
worker.addEventListener("close", (event) => {
console.log("exit code:", event.code);
});
smol 模式(内存优化)
内存受限时启用,牺牲性能降低内存占用:
ts
const worker = new Worker("./worker.ts", { smol: true });
// 将 JSC::HeapSize 从 Large 改为 Small
环境数据共享
ts
// 主线程
import { setEnvironmentData } from "worker_threads";
setEnvironmentData("config", { apiUrl: "https://api.example.com" });
// Worker
import { getEnvironmentData } from "worker_threads";
const config = getEnvironmentData("config");
监听 Worker 创建
ts
process.on("worker", (worker) => {
console.log("New worker:", worker.threadId);
});
判断当前线程
ts
if (Bun.isMainThread) {
console.log("主线程");
} else {
console.log("Worker 线程");
}
四、总结
| 对比项 | Bun 1.2.21 | Node.js 24.6.0 | 提升 |
|---|---|---|---|
| 3MB 字符串传输 | 593 ns | 242,110 ns | ~408 倍 |
| 简单对象传输(含 3MB 字符串) | 1.26 µs | 304 µs | ~241 倍 |
| 峰值内存 | 减少 22 倍 | --- | --- |
| 是否需要改代码 | 否(自动优化) | --- | --- |
Bun 通过一个优雅的技术洞察------字符串是线程安全的,无需序列化------让 postMessage 实现了数量级的性能飞跃。无论你是构建 API 网关、数据处理管道还是实时应用,Bun 的 Worker 都能带来实打实的性能红利。
📖 参考资料