Benchmarks
-
I Benchmarked JSON Parsing in Bun, Node, Rust, and Go
I’m just going to start posting about JSON everyday. Well ok, maybe not every day, but for the next few days at least. Later this week I’ve committed to writing a guide on getting started with CLIs for non-programmers, so stay tuned for that.
This morning I benchmarked JSON parsing across four runtimes: Bun, Node, Rust, and Go.
The Results
- Bun is the overall winner on large files — 307-354 MB/s, beating even Rust’s serde_json for untyped parsing
- Rust wins on small/nested data (225 MB/s small, 327 MB/s nested) due to low overhead
- Node is close behind Bun — V8’s JSON.parse is very optimized
- Go is ~3x slower than the JS runtimes on large payloads (encoding/json is notoriously slow)
- Memory: Bun reports 0 delta (likely GC reclaims before measurement), Rust’s tracking allocator shows the true heap cost (73-96MB), Go uses 52-65MB
Rust’s numbers were the most honest here since the tracking allocator catches everything. We should take Bun result with grain of salt because benchmarking memory in GC’d languages is tricky.
The json parser in v8 in node is the exact same as what is in Chrome…
Here’s the full test results if you want to dig into the numbers yourself.
More JSON content coming soon. You’ve been warned.
/ Programming / Bun / Node / Json / Benchmarks / Rust / Go
-
I Wrote Multiple CUE Parsers and Benchmarked Them Against JSON
It started yesterday because I wanted to fix JSON.
Well, not fix exactly. More like figure out if there was something better for client-side parsing in the browser. I’d been looking at comparing JSON versus MessagePack when I stumbled into CUE, a configuration language that embeds schema definitions directly in the data.
I posted about it and then was like well what if I just actually did it. There was just one problem: the core CUE spec is written in Go, which obviously doesn’t help when you need something running in React.
So I wrote a TypeScript parser for CUE. You can find it at cue-ts.
Then I built a benchmark app with 11 different parsing and deserialization approaches, all running client-side in a Vite + React app. We’ll see how that turned out here.
The Contenders
I tested across three ecosystems:
JSON: Native
JSON.parse, JSON + Zod validation, and JSON + Ajv (both pre-compiled and per-call schema compilation).CUE: Full AST parsing, a TypeScript deserializer, a fused single-pass scanner, pre-compiled CUE schemas, and even a Rust-to-WASM CUE parser.
Binary: MessagePack decoding, with and without Zod validation.
Payloads ranged from ~1KB to ~460KB across CUE, JSON, and MessagePack formats.
Understanding the Schema Problem
Before we look at numbers, we need to talk about schema validation.
With Zod, your schema is TypeScript code. It’s set at compile time, so there’s no runtime schema parsing overhead. With Ajv in compiled mode, it takes a JSON Schema document and generates an optimized JavaScript validation function. You do this once, and the runtime cost is essentially zero.
But CUE does something different. It embeds schema definitions directly in the data file:
#User: { email: string & =~"^[^@]+@[^@]+$" role: "admin" | "editor" | "viewer" age: int & >=0 } user: #User & { email: "[email protected]" role: "admin" age: 30 }Schema and data together, processed in one pass. Sounds elegant, right? The question is whether that elegance costs you speed. 😅
The Results
Here’s the 10KB payload benchmark on Chromium/V8:
Strategy Median vs JSON.parse JSON + Ajv (compiled) 18 μs ~equivalent JSON.parse 18 μs baseline JSON + Zod 28 μs 1.6x slower MsgPack Decode 30 μs 1.7x slower CUE (compiled schema) 112 μs 6.2x slower CUE Fast Deserialize 114 μs 6.3x slower CUE Deserialize (WASM) 755 μs 41.9x slower JSON + Ajv compiled is equivalent to bare
JSON.parse. The pre-compiled validator adds essentially zero overhead. Case closed?Not quite.
The Fair Fight
Those benchmarks aren’t comparing the same thing. Zod and Ajv compiled both pre-process their schemas before the benchmark runs. CUE processes its schema on every call. So I ran the fair comparison, schema processing included:
Strategy Median CUE Fast Deserialize 114 μs CUE (compiled schema) 112 μs JSON + Ajv (interpret) 4,840 μs When JSON has to compile its schema per call, CUE is 43x faster. Cool? All that work really paid off?
Well, the bottleneck for CUE isn’t schema processing. It’s parsing the text format itself. I tried basically every optimization I could think of. The deserializer spends nearly all its time scanning characters, building strings and numbers. Without native C++ code, which is what
JSON.parsegets for free from the browser engine, a TypeScript parser just can’t close that gap.MessagePack: Not Worth It
I included MessagePack benchmarks to see if smaller binary payloads would translate to faster parsing. They don’t. MessagePack is consistently slower than
JSON.parse, even with smaller payloads. The overhead of maintaining a separate binary serialization format on the client side just isn’t worth it for most use cases.Cross-Browser Highlights
I ran a few tests across engines. Some notable findings:
- Safari (JSC): Zod validation is essentially free. JSON + Zod matches bare
JSON.parse - Chrome (V8): CUE’s fast deserializer benefits from
charCodeAtoptimizations - Firefox (SpiderMonkey): Most consistent results across the board
So What’s the Answer?
If you can compile your schema ahead of time with Ajv or Zod, do that and use JSON.
It’s not even close. Seriously.
JSON.parsebenefits from decades of native browser optimization that no TypeScript parser can match, and pre-compiled validation adds essentially zero overhead.CUE is faster in one specific scenario: when you need to process schemas dynamically on every call. 43x faster than per-call Ajv compilation is real, but how often does that actually come up in production? It’s hard to say.
Use Case Pick This Why Hot-path APIs JSON + Ajv (compiled) Fastest possible TypeScript projects JSON + Zod Best DX Dynamic schemas CUE 43x faster than per-call Ajv Config files CUE Single source of truth Bandwidth-constrained MsgPack 30-60% smaller payloads One thing I considered but didn’t build for this post: a conversion layer that stores everything as CUE on the server, then compiles it down to JSON with Zod or Ajv schemas for the browser. You’d get CUE’s authoring experience with JSON’s runtime speed. That feels like an interesting project, and maybe I’ll get to it next.
Both the cue-ts parser and the benchmark app are open source. Try them yourself and let me know what you think.
/ Programming / Webdev / javascript / Benchmarks
- Safari (JSC): Zod validation is essentially free. JSON + Zod matches bare