Benchmarks for ECMAScript parsers compiled to native binaries (Zig, Rust), measuring raw parsing speed without any JavaScript runtime overhead.
| Property | Value |
|---|---|
| OS | macOS 24.6.0 (arm64) |
| CPU | Apple M3 |
| Cores | 8 |
| Memory | 16 GB |
Language: Zig
A high-performance & spec-compliant JavaScript/TypeScript compiler toolchain written in Zig.
Language: Rust
A high-performance JavaScript and TypeScript parser written in Rust.
Language: Rust
An extensible Rust-based platform for compiling and bundling JavaScript and TypeScript.
File size: 7.83 MB
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku | 22.04 ms | 21.76 ms | 22.49 ms | 1.00× |
| Oxc | 23.94 ms | 23.82 ms | 24.23 ms | 1.09× |
| SWC | 41.21 ms | 40.68 ms | 41.91 ms | 1.87× |
File size: 2.95 MB
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku | 7.38 ms | 7.34 ms | 7.55 ms | 1.00× |
| Oxc | 7.73 ms | 7.67 ms | 7.99 ms | 1.05× |
| SWC | 13.51 ms | 13.16 ms | 15.64 ms | 1.83× |
File size: 0.07 MB
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku | 0.14 ms | 0.14 ms | 0.15 ms | 1.00× |
| Oxc | 0.16 ms | 0.16 ms | 0.17 ms | 1.15× |
| SWC | 0.27 ms | 0.27 ms | 0.28 ms | 1.94× |
The ECMAScript specification defines a set of early errors that conformant implementations must report before execution. Some of these are detectable during parsing from local context alone, like return outside a function, yield outside a generator, invalid destructuring, etc. Others require knowledge of the program's scope structure and bindings, such as redeclarations, unresolved exports, private fields used outside their class, etc.
Parsers handle this differently: SWC checks some scope-dependent errors during parsing itself, while Yuku and Oxc defer them entirely to a separate semantic analysis pass. This keeps parsing fast and lets each consumer opt in only to the work it actually needs. A formatter, for example, only needs the AST and should not pay the cost of scope resolution.
The benchmarks below measure parsing followed by this additional pass, which builds a scope tree and symbol table, resolves identifier references to their declarations, and reports the remaining early errors. Together, parsing and semantic analysis cover the full set of early errors required by the specification.
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku + Semantic | 39.54 ms | 39.25 ms | 40.57 ms | 1.00× |
| Oxc + Semantic | 55.01 ms | 53.07 ms | 56.58 ms | 1.39× |
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku + Semantic | 12.93 ms | 12.84 ms | 13.49 ms | 1.00× |
| Oxc + Semantic | 18.11 ms | 17.87 ms | 36.31 ms | 1.40× |
| Parser | Median | Min | p99 | Relative |
|---|---|---|---|---|
| Yuku + Semantic | 0.27 ms | 0.26 ms | 0.28 ms | 1.00× |
| Oxc + Semantic | 0.34 ms | 0.34 ms | 0.36 ms | 1.28× |
- Bun - JavaScript runtime and package manager
- Rust - For building Rust-based parsers
- Zig - For building Zig-based parsers (requires nightly/development version)
- Clone the repository:
git clone https://github.com/yuku-toolchain/ecmascript-parser-benchmark-native.git
cd ecmascript-parser-benchmark-native- Install dependencies:
bun install- Run benchmarks:
bun benchThis will build all parsers and run benchmarks on all test files. Results are saved to the result/ directory.
Parsing is timed in-process to isolate it from process startup, dynamic linking, file I/O, and memory teardown, which would otherwise dominate the measurement on smaller files.
The source is read once, then each parser runs 50 warmup iterations followed by 300 timed iterations. A monotonic clock wraps only the parse call (plus the semantic pass for the semantic variants); allocation and teardown happen outside the timed region, and the result passes through an optimization barrier so the work cannot be elided. Reported figures are the median, minimum, and 99th percentile of the timed runs.
Binaries are built with release optimizations: Rust with cargo build --release (LTO, single codegen unit, symbol stripping) and Zig with zig build --release=fast. Each uses a fast general-purpose allocator (Rust mimalloc, Zig smp_allocator).





