Skip to content

Commit 1b1c4ad

Browse files
bench(protobuf): compare each runtime's real path, not forced protobuf-es
esrun now decodes with its native runtime:serialization Protobuf; Node/Bun/Deno keep protobuf-es. Same wire bytes, so the row reflects each runtime's actual protobuf handling. Regenerated site numbers: esrun leads on time and memory (small 77ms/37MB, large 4.1s/108MB).
1 parent 331e644 commit 1b1c4ad

5 files changed

Lines changed: 99 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ pre-`0.1.0` and the public API is unstable.
2626
name now covers both text and binary serialization formats; the exported
2727
namespaces (`XML`/`YAML`/`TOML`/`JSONL`/`MessagePack`) and their APIs are
2828
unchanged — only the import specifier moves.
29+
- **Protobuf benchmark now compares each runtime's real path** instead of forcing
30+
protobuf-es on every runtime: esrun decodes with its native
31+
`runtime:serialization` Protobuf, Node/Bun/Deno with protobuf-es. esrun leads on
32+
both time and memory (small 77ms/37MB, large 4.1s/108MB).
2933

3034
### Fixed
3135

bench/scripts/protobuf_large.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
1-
// Protobuf decode (large payload). Same library — protobuf-es (@bufbuild/protobuf)
2-
// — on every runtime, so this measures the runtime, not the library.
3-
import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
4-
import { CatalogSchema } from "../gen/catalog_pb.js";
1+
// Protobuf decode (large payload). Real-world positioning: esrun decodes with its
2+
// native runtime:serialization Protobuf (pure-JS, reflective); Node/Bun/Deno decode
3+
// with protobuf-es (@bufbuild/protobuf) — each runtime uses the lib it would ship.
4+
// The wire bytes are identical, so this compares each runtime's actual protobuf path.
5+
const PROTO = `
6+
syntax = "proto3";
7+
package test;
8+
message Book {
9+
string id = 1; string author = 2; string title = 3; string genre = 4;
10+
double price = 5; string publish_date = 6; string description = 7;
11+
}
12+
message Catalog { repeated Book catalog = 1; }
13+
`;
14+
15+
let schema = null;
16+
try {
17+
const { Protobuf } = await import("runtime:serialization");
18+
schema = new Protobuf.Schema(PROTO);
19+
} catch (e) {}
20+
const isEsrun = schema !== null;
21+
22+
let create, toBinary, fromBinary, CatalogSchema;
23+
if (!isEsrun) {
24+
({ create, toBinary, fromBinary } = await import("@bufbuild/protobuf"));
25+
({ CatalogSchema } = await import("../gen/catalog_pb.js"));
26+
}
527

628
const obj = { catalog: [] };
729
for (let i = 0; i < 50000; i++) {
@@ -16,8 +38,14 @@ for (let i = 0; i < 50000; i++) {
1638
});
1739
}
1840

19-
const protoBytes = toBinary(CatalogSchema, create(CatalogSchema, obj));
20-
const parseProtobuf = () => fromBinary(CatalogSchema, protoBytes);
41+
let parseProtobuf;
42+
if (isEsrun) {
43+
const protoBytes = schema.build("test.Catalog", obj);
44+
parseProtobuf = () => schema.parse("test.Catalog", protoBytes);
45+
} else {
46+
const protoBytes = toBinary(CatalogSchema, create(CatalogSchema, obj));
47+
parseProtobuf = () => fromBinary(CatalogSchema, protoBytes);
48+
}
2149

2250
// Warmup
2351
for (let i = 0; i < 5; i++) parseProtobuf();

bench/scripts/protobuf_small.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
1-
// Protobuf decode (small payload). Same library — protobuf-es (@bufbuild/protobuf)
2-
// — on every runtime, so this measures the runtime, not the library.
3-
import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
4-
import { CatalogSchema } from "../gen/catalog_pb.js";
1+
// Protobuf decode (small payload). Real-world positioning: esrun decodes with its
2+
// native runtime:serialization Protobuf (pure-JS, reflective); Node/Bun/Deno decode
3+
// with protobuf-es (@bufbuild/protobuf) — each runtime uses the lib it would ship.
4+
// The wire bytes are identical, so this compares each runtime's actual protobuf path.
5+
const PROTO = `
6+
syntax = "proto3";
7+
package test;
8+
message Book {
9+
string id = 1; string author = 2; string title = 3; string genre = 4;
10+
double price = 5; string publish_date = 6; string description = 7;
11+
}
12+
message Catalog { repeated Book catalog = 1; }
13+
`;
14+
15+
let schema = null;
16+
try {
17+
const { Protobuf } = await import("runtime:serialization");
18+
schema = new Protobuf.Schema(PROTO);
19+
} catch (e) {}
20+
const isEsrun = schema !== null;
21+
22+
let create, toBinary, fromBinary, CatalogSchema;
23+
if (!isEsrun) {
24+
({ create, toBinary, fromBinary } = await import("@bufbuild/protobuf"));
25+
({ CatalogSchema } = await import("../gen/catalog_pb.js"));
26+
}
527

628
const obj = { catalog: [] };
729
for (let i = 0; i < 50; i++) {
@@ -16,8 +38,14 @@ for (let i = 0; i < 50; i++) {
1638
});
1739
}
1840

19-
const protoBytes = toBinary(CatalogSchema, create(CatalogSchema, obj));
20-
const parseProtobuf = () => fromBinary(CatalogSchema, protoBytes);
41+
let parseProtobuf;
42+
if (isEsrun) {
43+
const protoBytes = schema.build("test.Catalog", obj);
44+
parseProtobuf = () => schema.parse("test.Catalog", protoBytes);
45+
} else {
46+
const protoBytes = toBinary(CatalogSchema, create(CatalogSchema, obj));
47+
parseProtobuf = () => fromBinary(CatalogSchema, protoBytes);
48+
}
2149

2250
// Warmup
2351
for (let i = 0; i < 5; i++) parseProtobuf();

site/app/docs/benchmarks/page.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ export default function BenchmarksDoc() {
182182
<BenchChart metrics={parserWorkloads} />
183183
</div>
184184

185-
<h2 className="mt-12 text-xl font-semibold text-zinc-900">Protobuf (same library, all runtimes)</h2>
185+
<h2 className="mt-12 text-xl font-semibold text-zinc-900">Protobuf (each runtime&apos;s own path)</h2>
186186
<p className="mt-2 text-sm text-zinc-500">
187-
Every runtime runs the same library — <code>protobuf-es</code> (<code>@bufbuild/protobuf</code>) — decoding identical bytes, so this measures the runtime, not the library.
187+
esrun decodes with its native <code>runtime:serialization</code> Protobuf (pure-JS, reflective); Node, Bun, and Deno decode with <code>protobuf-es</code> (<code>@bufbuild/protobuf</code>). Identical bytes, each runtime&apos;s real protobuf path.
188188
</p>
189189
<div className="mt-5">
190190
<BenchChart metrics={protobufWorkloads} />

site/src/benchmarks.js

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ export default {
1010
},
1111
"results_ms": {
1212
"startup": {
13-
"node": 17.2,
14-
"bun": 9,
15-
"deno": 23.7,
16-
"llrt": 3.7,
13+
"node": 17.4,
14+
"bun": 8.8,
15+
"deno": 24,
16+
"llrt": 3.6,
1717
"esrun": 7.2
1818
},
1919
"bigscript": {
20-
"node": 28.6,
21-
"bun": 20.6,
22-
"deno": 32.3,
23-
"llrt": 11.1,
24-
"esrun": 18.2
20+
"node": 28.9,
21+
"bun": 20.8,
22+
"deno": 32.5,
23+
"llrt": 11.2,
24+
"esrun": 18.3
2525
},
2626
"compute": {
2727
"node": 194.2,
@@ -297,18 +297,18 @@ export default {
297297
"esrun": 53.2
298298
},
299299
"protobuf_small": {
300-
"node": 124.7,
301-
"bun": 138.7,
302-
"deno": 187.9,
303-
"llrt": 1783.3,
304-
"esrun": 136.5
300+
"node": 120.9,
301+
"bun": 141.3,
302+
"deno": 186.6,
303+
"llrt": 1735.5,
304+
"esrun": 76.6
305305
},
306306
"protobuf_large": {
307-
"node": 5969.1,
308-
"bun": 6606,
309-
"deno": 9618.1,
307+
"node": 5879.9,
308+
"bun": 6382.5,
309+
"deno": 9507.1,
310310
"llrt": null,
311-
"esrun": 7417.2
311+
"esrun": 4144.3
312312
}
313313
},
314314
"results_rss": {
@@ -324,7 +324,7 @@ export default {
324324
"bun": 33,
325325
"deno": 55,
326326
"llrt": 11,
327-
"esrun": 21
327+
"esrun": 22
328328
},
329329
"compute": {
330330
"node": 53,
@@ -601,17 +601,17 @@ export default {
601601
},
602602
"protobuf_small": {
603603
"node": 66,
604-
"bun": 123,
604+
"bun": 118,
605605
"deno": 73,
606606
"llrt": 12,
607-
"esrun": 42
607+
"esrun": 37
608608
},
609609
"protobuf_large": {
610-
"node": 357,
611-
"bun": 227,
612-
"deno": 366,
610+
"node": 426,
611+
"bun": 223,
612+
"deno": 351,
613613
"llrt": null,
614-
"esrun": 146
614+
"esrun": 108
615615
}
616616
},
617617
"results_rps": {

0 commit comments

Comments
 (0)