Skip to content

Commit 9987b97

Browse files
authored
fix!: increase encoding/decoding performance (#58)
Switches the internal serialization/deserialization code to use the Reader/Writer from protobufjs as it's very fast and also the least interesting thing about this module. ```console % node packages/protons-benchmark/dist/src/encode.js pbjs x 1,067,457 ops/sec ±1.74% (83 runs sampled) protons x 1,165,064 ops/sec ±2.78% (90 runs sampled) protobufjs x 1,317,685 ops/sec ±2.73% (89 runs sampled) Fastest is protobufjs ``` ```console % node packages/protons-benchmark/dist/src/decode.js pbjs x 1,997,186 ops/sec ±0.87% (86 runs sampled) protons x 1,490,204 ops/sec ±2.40% (90 runs sampled) protobufjs x 1,809,549 ops/sec ±0.58% (88 runs sampled) Fastest is pbjs ``` ```console % node packages/protons-benchmark/dist/src/index.js pbjs x 11,307 ops/sec ±6.04% (86 runs sampled) protons x 11,376 ops/sec ±2.33% (80 runs sampled) protobufjs x 12,086 ops/sec ±1.84% (89 runs sampled) Fastest is protobufjs,pbjs ``` ```console % node packages/protons-benchmark/dist/src/rpc.js protons x 3,308,190 ops/sec ±0.57% (92 runs sampled) protobufjs x 3,135,946 ops/sec ±2.14% (91 runs sampled) Fastest is protons ``` BREAKING CHANGE: the exported types of `protons-runtime` have changed and protobuf encoders/decoders will need to be regenerated
1 parent dab81db commit 9987b97

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+7429
-832
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ node_modules
1212
package-lock.json
1313
yarn.lock
1414
.clinic
15+
coverage

lerna.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
{
22
"lerna": "4.0.0",
3-
"packages": [
4-
"packages/*"
5-
],
3+
"useWorkspaces": true,
64
"version": "independent",
75
"command": {
86
"run": {

packages/protons-benchmark/package.json

+7-4
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
"sourceType": "module"
5656
},
5757
"ignorePatterns": [
58-
"src/protobufjs/*.ts"
58+
"src/pbjs/*",
59+
"src/protobufjs/*"
5960
]
6061
},
6162
"scripts": {
@@ -67,12 +68,14 @@
6768
"start": "node dist/src/index.js"
6869
},
6970
"dependencies": {
71+
"@types/benchmark": "^2.1.1",
7072
"aegir": "^37.0.5",
71-
"benny": "^3.7.1",
73+
"benchmark": "^2.1.4",
7274
"pbjs": "^0.0.14",
73-
"protobufjs": "^6.11.2",
75+
"protobufjs": "^7.0.0",
7476
"protons": "^4.0.0",
75-
"protons-runtime": "^2.0.0"
77+
"protons-runtime": "^2.0.0",
78+
"uint8arrays": "^3.1.0"
7679
},
7780
"private": true
7881
}

packages/protons-benchmark/src/bench.proto

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
syntax = "proto3";
12

23
message Foo {
34
optional uint32 baz = 1;
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* eslint-disable no-console */
2+
3+
/*
4+
$ node dist/src/index.js
5+
$ npx playwright-test dist/src/index.js --runner benchmark
6+
*/
7+
8+
import Benchmark from 'benchmark'
9+
import { Test as ProtonsTest } from './protons/bench.js'
10+
import { decodeTest as pbjsDecodeTest } from './pbjs/bench.js'
11+
import { Test as ProtobufjsTest } from './protobufjs/bench.js'
12+
13+
const message = {
14+
meh: {
15+
lol: 'sdkljfoee',
16+
b: {
17+
tmp: {
18+
baz: 2309292
19+
}
20+
}
21+
},
22+
hello: 3493822,
23+
foo: 'derp derp derp',
24+
payload: Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
25+
}
26+
27+
const buf = ProtonsTest.encode(message).subarray()
28+
29+
new Benchmark.Suite()
30+
.add('pbjs', () => {
31+
pbjsDecodeTest(buf)
32+
})
33+
.add('protons', () => {
34+
ProtonsTest.decode(buf)
35+
})
36+
.add('protobufjs', () => {
37+
ProtobufjsTest.decode(buf)
38+
})
39+
.on('error', (err: Error) => {
40+
console.error(err)
41+
})
42+
.on('cycle', (event: any) => {
43+
console.info(String(event.target))
44+
})
45+
.on('complete', function () {
46+
// @ts-expect-error types are wrong
47+
console.info(`Fastest is ${this.filter('fastest').map('name')}`) // eslint-disable-line @typescript-eslint/restrict-template-expressions
48+
})
49+
// run async
50+
.run({ async: true })
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* eslint-disable no-console */
2+
3+
/*
4+
$ node dist/src/index.js
5+
$ npx playwright-test dist/src/index.js --runner benchmark
6+
*/
7+
8+
import Benchmark from 'benchmark'
9+
import { Test as ProtonsTest } from './protons/bench.js'
10+
import { encodeTest as pbjsEncodeTest } from './pbjs/bench.js'
11+
import { Test as ProtobufjsTest } from './protobufjs/bench.js'
12+
13+
const message = {
14+
meh: {
15+
lol: 'sdkljfoee',
16+
b: {
17+
tmp: {
18+
baz: 2309292
19+
}
20+
}
21+
},
22+
hello: 3493822,
23+
foo: 'derp derp derp',
24+
payload: Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
25+
}
26+
27+
new Benchmark.Suite()
28+
.add('pbjs', () => {
29+
pbjsEncodeTest(message)
30+
})
31+
.add('protons', () => {
32+
ProtonsTest.encode(message)
33+
})
34+
.add('protobufjs', () => {
35+
ProtobufjsTest.encode(message).finish()
36+
})
37+
.on('error', (err: Error) => {
38+
console.error(err)
39+
})
40+
.on('cycle', (event: any) => {
41+
console.info(String(event.target))
42+
})
43+
.on('complete', function () {
44+
// @ts-expect-error types are wrong
45+
console.info(`Fastest is ${this.filter('fastest').map('name')}`) // eslint-disable-line @typescript-eslint/restrict-template-expressions
46+
})
47+
// run async
48+
.run({ async: true })

packages/protons-benchmark/src/index.ts

+26-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
/* eslint-disable no-console */
12

2-
import benny from 'benny'
3+
/*
4+
$ node dist/src/index.js
5+
$ npx playwright-test dist/src/index.js --runner benchmark
6+
*/
7+
8+
import Benchmark from 'benchmark'
39
import { expect } from 'aegir/chai'
410
import { Test as ProtonsTest } from './protons/bench.js'
511
import { encodeTest as pbjsEncodeTest, decodeTest as pbjsDecodeTest } from './pbjs/bench.js'
@@ -27,30 +33,34 @@ function expectDecodedCorrectly (result: any) {
2733
expect(result).to.have.property('payload').that.equalBytes(message.payload)
2834
}
2935

30-
void benny.suite(
31-
'Encode/Decode',
32-
33-
benny.add('pbjs', () => {
36+
new Benchmark.Suite()
37+
.add('pbjs', () => {
3438
const buf = pbjsEncodeTest(message)
3539
const result = pbjsDecodeTest(buf)
3640

3741
expectDecodedCorrectly(result)
38-
}),
39-
40-
benny.add('protons', () => {
42+
})
43+
.add('protons', () => {
4144
const buf = ProtonsTest.encode(message)
4245
const result = ProtonsTest.decode(buf)
4346

4447
expectDecodedCorrectly(result)
45-
}),
46-
47-
benny.add('protobufjs', () => {
48+
})
49+
.add('protobufjs', () => {
4850
const buf = ProtobufjsTest.encode(message).finish()
4951
const result = ProtobufjsTest.decode(buf)
5052

5153
expectDecodedCorrectly(result)
52-
}),
53-
54-
benny.cycle(),
55-
benny.complete()
56-
)
54+
})
55+
.on('error', (err: Error) => {
56+
console.error(err)
57+
})
58+
.on('cycle', (event: any) => {
59+
console.info(String(event.target))
60+
})
61+
.on('complete', function () {
62+
// @ts-expect-error types are wrong
63+
console.info(`Fastest is ${this.filter('fastest').map('name')}`) // eslint-disable-line @typescript-eslint/restrict-template-expressions
64+
})
65+
// run async
66+
.run({ async: true })

0 commit comments

Comments
 (0)