Skip to content

Commit 82cfa14

Browse files
committed
feat: compute buffer size ahead of time
The current implementation loops over message fields and each field encoder creates a `Uint8Array` which is added to a `Uint8ArrayList` and stitched together at the end of the process. Other implementations compute the expected length of the serialized message, allocate a buffer that size then have encoders write their data into it at the correct offsets. This PR makes protons compute the final message buffer size first in the same way. Thing is, it doesn't seem to make a huge amount of difference to performance. Before: ``` Running "Encode/Decode" suite... Progress: 100% pbjs: 12 166 ops/s, ±3.92% | 5.12% slower protons: 9 755 ops/s, ±2.19% | slowest, 23.93% slower protobufjs: 12 823 ops/s, ±2.02% | fastest Finished 3 cases! Fastest: protobufjs Slowest: protons ``` After: ``` Running "Encode/Decode" suite... Progress: 100% pbjs: 11 866 ops/s, ±3.43% | 2.05% slower protons: 9 356 ops/s, ±2.45% | slowest, 22.77% slower protobufjs: 12 114 ops/s, ±2.16% | fastest Finished 3 cases! Fastest: protobufjs Slowest: protons ```
1 parent aff19cc commit 82cfa14

23 files changed

+132
-147
lines changed

packages/protons-runtime/src/codecs/bool.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ const encodingLength: EncodingLengthFunction<boolean> = function boolEncodingLen
44
return 1
55
}
66

7-
const encode: EncodeFunction<boolean> = function boolEncode (value) {
8-
return Uint8Array.from([value ? 1 : 0])
7+
const encode: EncodeFunction<boolean> = function boolEncode (val, buf, offset) {
8+
buf.set(offset, val ? 1 : 0)
9+
10+
return offset + encodingLength(val)
911
}
1012

1113
const decode: DecodeFunction<boolean> = function boolDecode (buffer, offset) {

packages/protons-runtime/src/codecs/bytes.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11

2-
import { Uint8ArrayList } from 'uint8arraylist'
32
import { unsigned } from '../utils/varint.js'
43
import { DecodeFunction, EncodeFunction, createCodec, EncodingLengthFunction, CODEC_TYPES } from './codec.js'
54

@@ -8,12 +7,11 @@ const encodingLength: EncodingLengthFunction<Uint8Array> = function bytesEncodin
87
return unsigned.encodingLength(len) + len
98
}
109

11-
const encode: EncodeFunction<Uint8Array> = function bytesEncode (val) {
12-
const prefix = new Uint8Array(unsigned.encodingLength(val.byteLength))
10+
const encode: EncodeFunction<Uint8Array> = function bytesEncode (val, buf, offset) {
11+
offset = unsigned.encode(val.byteLength, buf, offset)
12+
buf.write(val, offset)
1313

14-
unsigned.encode(val.byteLength, prefix)
15-
16-
return new Uint8ArrayList(prefix, val)
14+
return offset + val.byteLength
1715
}
1816

1917
const decode: DecodeFunction<Uint8Array> = function bytesDecode (buf, offset) {

packages/protons-runtime/src/codecs/codec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export enum CODEC_TYPES {
1111
}
1212

1313
export interface EncodeFunction<T> {
14-
(value: T): Uint8Array | Uint8ArrayList
14+
(value: T, buf: Uint8ArrayList, offset: number): number
1515
}
1616

1717
export interface DecodeFunction<T> {

packages/protons-runtime/src/codecs/double.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { Uint8ArrayList } from 'uint8arraylist'
21
import { DecodeFunction, EncodeFunction, createCodec, EncodingLengthFunction, CODEC_TYPES } from './codec.js'
32

43
const encodingLength: EncodingLengthFunction<number> = function doubleEncodingLength () {
54
return 8
65
}
76

8-
const encode: EncodeFunction<number> = function doubleEncode (val) {
9-
const buf = new Uint8ArrayList(new Uint8Array(encodingLength(val)))
10-
buf.setFloat64(0, val, true)
7+
const encode: EncodeFunction<number> = function doubleEncode (val, buf, offset) {
8+
buf.setFloat64(offset, val, true)
119

12-
return buf
10+
return offset + encodingLength(val)
1311
}
1412

1513
const decode: DecodeFunction<number> = function doubleDecode (buf, offset) {

packages/protons-runtime/src/codecs/enum.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ export function enumeration <T> (e: T): Codec<T> {
1010
return unsigned.encodingLength(index)
1111
}
1212

13-
const encode: EncodeFunction<string> = function enumEncode (val) {
13+
const encode: EncodeFunction<string> = function enumEncode (val, buf, offset) {
1414
const keys = Object.keys(e)
1515
const index = keys.indexOf(val)
16-
const buf = new Uint8Array(unsigned.encodingLength(index))
1716

18-
unsigned.encode(index, buf)
19-
20-
return buf
17+
return unsigned.encode(index, buf, offset)
2118
}
2219

2320
const decode: DecodeFunction<string> = function enumDecode (buf, offset) {

packages/protons-runtime/src/codecs/fixed32.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { Uint8ArrayList } from 'uint8arraylist'
21
import { DecodeFunction, EncodeFunction, createCodec, EncodingLengthFunction, CODEC_TYPES } from './codec.js'
32

43
const encodingLength: EncodingLengthFunction<number> = function fixed32EncodingLength () {
54
return 4
65
}
76

8-
const encode: EncodeFunction<number> = function fixed32Encode (val) {
9-
const buf = new Uint8ArrayList(new Uint8Array(encodingLength(val)))
10-
buf.setInt32(0, val, true)
7+
const encode: EncodeFunction<number> = function fixed32Encode (val, buf, offset) {
8+
buf.setInt32(offset, val, true)
119

12-
return buf
10+
return offset + encodingLength(val)
1311
}
1412

1513
const decode: DecodeFunction<number> = function fixed32Decode (buf, offset) {

packages/protons-runtime/src/codecs/fixed64.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { Uint8ArrayList } from 'uint8arraylist'
21
import { DecodeFunction, EncodeFunction, createCodec, EncodingLengthFunction, CODEC_TYPES } from './codec.js'
32

43
const encodingLength: EncodingLengthFunction<bigint> = function int64EncodingLength (val) {
54
return 8
65
}
76

8-
const encode: EncodeFunction<bigint> = function int64Encode (val) {
9-
const buf = new Uint8ArrayList(new Uint8Array(encodingLength(val)))
10-
buf.setBigInt64(0, val, true)
7+
const encode: EncodeFunction<bigint> = function int64Encode (val, buf, offset) {
8+
buf.setBigInt64(offset, val, true)
119

12-
return buf
10+
return offset + encodingLength(val)
1311
}
1412

1513
const decode: DecodeFunction<bigint> = function int64Decode (buf, offset) {

packages/protons-runtime/src/codecs/float.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { Uint8ArrayList } from 'uint8arraylist'
21
import { DecodeFunction, EncodeFunction, createCodec, EncodingLengthFunction, CODEC_TYPES } from './codec.js'
32

43
const encodingLength: EncodingLengthFunction<number> = function floatEncodingLength () {
54
return 4
65
}
76

8-
const encode: EncodeFunction<number> = function floatEncode (val) {
9-
const buf = new Uint8ArrayList(new Uint8Array(encodingLength(1)))
10-
buf.setFloat32(0, val, true)
7+
const encode: EncodeFunction<number> = function floatEncode (val, buf, offset) {
8+
buf.setFloat32(offset, val, true)
119

12-
return buf
10+
return offset + encodingLength(val)
1311
}
1412

1513
const decode: DecodeFunction<number> = function floatDecode (buf, offset) {

packages/protons-runtime/src/codecs/int32.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ const encodingLength: EncodingLengthFunction<number> = function int32EncodingLen
55
return signed.encodingLength(val)
66
}
77

8-
const encode: EncodeFunction<number> = function int32Encode (val) {
9-
const buf = new Uint8Array(encodingLength(val))
10-
signed.encode(val, buf)
11-
12-
return buf
8+
const encode: EncodeFunction<number> = function int32Encode (val, buf, offset) {
9+
return signed.encode(val, buf, offset)
1310
}
1411

1512
const decode: DecodeFunction<number> = function int32Decode (buf, offset) {

packages/protons-runtime/src/codecs/int64.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ const encodingLength: EncodingLengthFunction<bigint> = function int64EncodingLen
55
return signed.encodingLength(val)
66
}
77

8-
const encode: EncodeFunction<bigint> = function int64Encode (val) {
9-
const buf = new Uint8Array(encodingLength(val))
10-
signed.encode(val, buf)
11-
12-
return buf
8+
const encode: EncodeFunction<bigint> = function int64Encode (val, buf, offset) {
9+
return signed.encode(val, buf, offset)
1310
}
1411

1512
const decode: DecodeFunction<bigint> = function int64Decode (buf, offset) {

0 commit comments

Comments
 (0)