Perry 对比 Bun
Bun 是用 Zig 编写的一体化 JavaScript/TypeScript 运行时、打包器、包管理器和测试运行器,它也可以通过把自身运行时与你的代码打包来生成单文件可执行程序。Perry 走的是另一条路:通过 LLVM 把 TypeScript 直接编译为原生机器码——二进制中没有 JavaScript 引擎、没有运行时,只有一个体积很小的原生可执行文件。Bun 与 Perry 在 TS 转二进制这一点上有重叠,但在“二进制里是否要带一个 JavaScript 引擎”上意见相左。
什么是 Bun?
Bun 是一个用 Zig 编写的快速、一体化的 JavaScript 与 TypeScript 工具链。它直接运行 `.ts` 源码(无需预编译),使用 JavaScriptCore 作为 JS 引擎,并自带打包器、包管理器和测试运行器。`bun build --compile` 会把 Bun 运行时连同你的应用一起打包为单个可执行文件。Bun 面向 Linux、macOS 和 Windows 上的 x64 与 arm64。
什么是 Perry?
Perry 是一个用 Rust 编写的原生 TypeScript 编译器。它通过 LLVM 把 TypeScript 直接编译为原生机器码——没有 JavaScript 引擎、没有运行时、没有 JIT。产物是单个二进制文件(hello world 大约 330 KB;带完整标准库的应用,例如 Fastify 服务器,约 48 MB)。Perry 面向 10 个平台,包括 macOS、iOS、iPadOS、Android、Linux、Windows、watchOS、tvOS、WebAssembly 和 Web。
并排对比
| 特性 | Perry | Bun |
|---|---|---|
| 产物 | 原生机器码(LLVM) | 你的代码 + Bun 运行时打包在一个二进制中 |
| 二进制中的 JavaScript 引擎 | 默认无;可选 V8,通过 --enable-js-runtime 启用(+约 15 MB) | 始终内置 JavaScriptCore |
| hello world 二进制大小 | 约 330 KB | 约 50–100 MB(包含 Bun 运行时) |
| JIT | 无(AOT 编译) | 有(JavaScriptCore JIT) |
| 移动平台目标(iOS、Android) | 支持——通过 UIKit/JNI 提供原生 UI | 不支持 |
| Watch / TV 目标 | watchOS、tvOS、Wear OS | 不支持 |
| 原生 UI 组件 | 通过 AppKit、UIKit、GTK4、Win32、JNI 提供 25+ 个 | 无(聚焦服务端/CLI) |
| npm 生态 | 纯 TS/JS 包可原生编译;其他通过可选 V8 | 完整的 Node 兼容 npm |
| 稳定性 | Pre-1.0(alpha) | 稳定(1.x) |
| 实现语言 | Rust | Zig |
Perry 胜出之处
- +更小的二进制——Perry 的 hello world 约 330 KB;Bun --compile 的 hello world 包含 Bun 运行时,落在数十 MB 量级。
- +无 JavaScript 引擎成本。Perry 编译出的二进制不携带解释器或 JIT——你的 TypeScript 就是可执行文件本身。
- +移动、手表、TV。Perry 可以编译到 iOS、iPadOS、Android、watchOS、tvOS 和 WebAssembly。Bun 仅限服务端/桌面。
- +原生 UI。Perry 的 perry/ui 模块可编译到真正的平台组件(macOS 上的 AppKit,iOS 上的 UIKit,Linux 上的 GTK4,Windows 上的 Win32,Android 上的 JNI)。Bun 没有 UI 方案。
- +在 M1 Max 上以匹配条件实测的多数计算微基准更快(RUNS=11,v0.5.279,2026-04-25):fibonacci 318 ms vs Bun 589 ms;object_create 1 ms vs 6 ms;nested_loops 18 ms vs 21 ms。完整数据见 perry/benchmarks。
- +在动态类型运行时阵营的 JSON validate-and-roundtrip 上更快:在同一份 1 万条记录的负载上,Perry 的惰性 JSON tape 中位数 75 ms,Bun 为 259 ms。
Bun 胜出之处
- +成熟、稳定、已发布 1.x 版本的运行时。Perry 仍处于 Pre-1.0。
- +在 JSON parse-and-iterate(每个值都会被访问的场景)上更快:同一负载下 Bun 254 ms,Perry 中位数 466 ms——一旦强制遍历,Perry 的惰性 tape 无法走捷径。
- +开箱即用、完整的 Node 兼容 npm 生态。Perry 原生支持其中一个子集,其余则回退到可选嵌入的 V8。
- +内置测试运行器、打包器和包管理器。Perry 是一个编译器,相邻工具链是分开的。
- +在带有热点内层循环、迭代密集的代码上,JIT 预热后的性能可以超过 AOT;Perry 没有 JIT。
- +在 `loop_data_dependent` 上与 Perry 在多次运行噪声范围内打成平手(232 ms vs 235 ms)——这是真正不可折叠的 f64 内核,双方编译器都无法重排序。来源:perry/benchmarks RUNS=11。
何时选择 Perry
在以下情况选择 Perry:二进制大小很重要(移动端分发、嵌入式场景、快速冷启动),希望从一份 TypeScript 代码同时交付到移动/手表/TV,希望使用原生 UI 组件,或完全不希望最终交付物里带 JavaScript 引擎。
何时选择 Bun
在以下情况选择 Bun:你今天就需要一个稳定、成熟的运行时;完整的 npm 兼容不可妥协;你想用一个工具同时承担运行时 + 打包器 + 包管理器 + 测试运行器;或者长时间运行负载下的 JIT 热态性能比冷启动体积更重要。
结论
Bun 和 Perry 都让你把 TypeScript 程序作为单个二进制交付,但它们回答的是不同的问题。Bun 的二进制内含 Bun 运行时,针对后端/CLI 场景做了优化,靠 JIT 与完整的 Node 兼容取胜。Perry 的二进制不含 JS 引擎,针对体积、冷启动、移动端和原生 UI 做了优化。如果你交付的是服务器,Bun 在今天更经得起考验;如果你交付的是原生应用或在意二进制大小,Perry 就是为这种场景而生的。