Skip to content

Commit d83f799

Browse files
committed
More (shameless) writer over-optimization
1 parent 8a2dbc6 commit d83f799

File tree

11 files changed

+324
-202
lines changed

11 files changed

+324
-202
lines changed

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -364,33 +364,33 @@ The package includes a [benchmark](https://github.com/dcodeIO/protobuf.js/tree/m
364364
```
365365
benchmarking encoding performance ...
366366
367-
Type.encode to buffer x 514,048 ops/sec ±0.75% (93 runs sampled)
368-
JSON.stringify to string x 355,935 ops/sec ±0.79% (91 runs sampled)
369-
JSON.stringify to buffer x 191,023 ops/sec ±1.39% (86 runs sampled)
367+
Type.encode to buffer x 469,818 ops/sec ±0.58% (89 runs sampled)
368+
JSON.stringify to string x 309,883 ops/sec ±0.81% (92 runs sampled)
369+
JSON.stringify to buffer x 174,440 ops/sec ±1.46% (87 runs sampled)
370370
371371
Type.encode to buffer was fastest
372-
JSON.stringify to string was 30.8% slower
373-
JSON.stringify to buffer was 63.1% slower
372+
JSON.stringify to string was 34.2% slower
373+
JSON.stringify to buffer was 63.2% slower
374374
375375
benchmarking decoding performance ...
376376
377-
Type.decode from buffer x 1,238,587 ops/sec ±1.73% (87 runs sampled)
378-
JSON.parse from string x 312,168 ops/sec ±2.22% (83 runs sampled)
379-
JSON.parse from buffer x 272,975 ops/sec ±2.45% (82 runs sampled)
377+
Type.decode from buffer x 976,639 ops/sec ±0.81% (91 runs sampled)
378+
JSON.parse from string x 294,282 ops/sec ±0.69% (89 runs sampled)
379+
JSON.parse from buffer x 263,440 ops/sec ±0.84% (93 runs sampled)
380380
381381
Type.decode from buffer was fastest
382-
JSON.parse from string was 74.9% slower
383-
JSON.parse from buffer was 78.1% slower
382+
JSON.parse from string was 69.8% slower
383+
JSON.parse from buffer was 73.0% slower
384384
385385
benchmarking combined performance ...
386386
387-
Type to/from buffer x 246,428 ops/sec ±1.52% (89 runs sampled)
388-
JSON to/from string x 136,380 ops/sec ±1.50% (80 runs sampled)
389-
JSON to/from buffer x 95,229 ops/sec ±1.93% (86 runs sampled)
387+
Type to/from buffer x 205,029 ops/sec ±1.59% (88 runs sampled)
388+
JSON to/from string x 127,198 ops/sec ±0.76% (91 runs sampled)
389+
JSON to/from buffer x 90,980 ops/sec ±0.77% (91 runs sampled)
390390
391391
Type to/from buffer was fastest
392-
JSON to/from string was 44.6% slower
393-
JSON to/from buffer was 61.5% slower
392+
JSON to/from string was 37.5% slower
393+
JSON to/from buffer was 55.3% slower
394394
```
395395

396396
Note that JSON is a native binding nowadays and as such is *really* fast. So, how can protobuf.js be faster?

bench/write.js

Lines changed: 172 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,197 @@
11
var protobuf = require("../src/index"),
2-
newSuite = require("./suite");
2+
newSuite = require("./suite"),
3+
ieee754 = require("../lib/ieee754");
34

45
// This benchmark compares raw data type performance of Uint8Array and Buffer.
5-
// This uses internal finish machinery because overall difference is otherwise mainly caused by allocation.
66

7-
var array = new Uint8Array(8);
7+
/* var array = new Uint8Array(8);
88
var buffer = new Buffer(8);
99
10-
// The following is a possible alternative to float / double writing - where supported. Quite a bit faster.
11-
12-
var f32 = new Float32Array(1);
13-
var i8 = new Uint8Array(f32.buffer);
10+
// raw fixed32 write speed
11+
newSuite("fixed32")
12+
.add("shift array", function() {
13+
var val = 0x7fffffff;
14+
array[0] = val & 255;
15+
array[1] = val >>> 8 & 255;
16+
array[2] = val >>> 16 & 255;
17+
array[3] = val >>> 24;
18+
})
19+
.add("shift buffer", function() {
20+
var val = 0x7fffffff;
21+
buffer[0] = val & 255;
22+
buffer[1] = val >>> 8 & 255;
23+
buffer[2] = val >>> 16 & 255;
24+
buffer[3] = val >>> 24;
25+
})
26+
.add("writeUInt32LE buffer", function() {
27+
var val = 0x7fffffff;
28+
buffer.writeUInt32LE(val, 0, true);
29+
})
30+
.run();
1431
15-
function writeF32Array(buf, pos, val) {
16-
f32[0] = val;
17-
for (var i = 0; i < 4; ++i)
18-
buf[pos + i] = i8[i];
19-
}
32+
var f64 = new Float64Array(1);
33+
var f32 = new Float32Array(f64.buffer);
34+
var f8b = new Uint8Array(f64.buffer);
2035
2136
// raw float write speed
2237
newSuite("float")
23-
.add("Writer#float", function() {
24-
var writer = new protobuf.Writer();
25-
writer.float(0.1);
26-
writer._finish(writer.head, array);
27-
})
28-
.add("Writer#writeF32Array", function() {
29-
var writer = new protobuf.Writer();
30-
writer.push(writeF32Array, 4, 0.1);
31-
writer._finish(writer.head, array);
32-
})
33-
.add("BufferWriter#float", function() {
34-
var writer = new protobuf.BufferWriter();
35-
writer.float(0.1);
36-
writer._finish(writer.head, buffer);
37-
})
38-
.add("BufferWriter#writeF32Array", function() {
39-
var writer = new protobuf.BufferWriter();
40-
writer.push(writeF32Array, 4, 0.1);
41-
writer._finish(writer.head, buffer);
38+
.add("ieee754 array", function() {
39+
ieee754.write(array, 0.1, 0, false, 23, 4);
40+
})
41+
.add("ieee754 buffer", function() {
42+
ieee754.write(buffer, 0.1, 0, false, 23, 4);
43+
})
44+
.add("f32 array", function() {
45+
f32[0] = 0.1;
46+
array[0] = f8b[0];
47+
array[0+1] = f8b[1];
48+
array[0+2] = f8b[2];
49+
array[0+3] = f8b[3];
50+
})
51+
.add("f32 buffer", function() {
52+
f32[0] = 0.1;
53+
buffer[0] = f8b[0];
54+
buffer[0+1] = f8b[1];
55+
buffer[0+2] = f8b[2];
56+
buffer[0+3] = f8b[3];
57+
})
58+
.add("writeFloatLE buffer", function() {
59+
buffer.writeFloatLE(0.1, 0, true);
4260
})
4361
.run();
4462
4563
// raw double write speed
4664
newSuite("double")
47-
.add("Writer#double", function() {
48-
var writer = new protobuf.Writer();
49-
writer.double(0.1);
50-
writer._finish(writer.head, array);
65+
.add("ieee754 array", function() {
66+
ieee754.write(array, 0.1, 0, false, 52, 8);
67+
})
68+
.add("ieee754 buffer", function() {
69+
ieee754.write(buffer, 0.1, 0, false, 52, 8);
5170
})
52-
.add("BufferWriter#double", function() {
53-
var writer = new protobuf.BufferWriter();
54-
writer.double(0.1);
55-
writer._finish(writer.head, buffer);
71+
.add("f64 array", function() {
72+
f64[0] = 0.1;
73+
array[0] = f8b[0];
74+
array[0+1] = f8b[1];
75+
array[0+2] = f8b[2];
76+
array[0+3] = f8b[3];
77+
array[0+4] = f8b[4];
78+
array[0+5] = f8b[5];
79+
array[0+6] = f8b[6];
80+
array[0+7] = f8b[7];
81+
})
82+
.add("f64 buffer", function() {
83+
f64[0] = 0.1;
84+
buffer[0] = f8b[0];
85+
buffer[0+1] = f8b[1];
86+
buffer[0+2] = f8b[2];
87+
buffer[0+3] = f8b[3];
88+
buffer[0+4] = f8b[4];
89+
buffer[0+5] = f8b[5];
90+
buffer[0+6] = f8b[6];
91+
buffer[0+7] = f8b[7];
92+
})
93+
.add("writeDoubleLE buffer", function() {
94+
buffer.writeDoubleLE(0.1, 0, true);
5695
})
5796
.run();
5897
59-
var arrayPlus1 = new Uint8Array(array.length + 1);
60-
var bufferPlus1 = new Buffer(buffer.length + 1);
98+
var source = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
99+
var array = new Uint8Array(16);
100+
var buffer = new Buffer(16);
61101
62102
// raw bytes write speed
63-
// interestingly, Uint8Array#set is faster than Buffer#copy, but only on actual Uint8Arrays.
64103
newSuite("bytes")
65-
.add("Writer#bytes", function() {
66-
var writer = new protobuf.Writer();
67-
writer.bytes(array);
68-
writer._finish(writer.head, arrayPlus1);
104+
.add("set array", function() {
105+
array.set(source, 0);
106+
})
107+
.add("for array", function() {
108+
for (var i = 0; i < source.length; ++i)
109+
array[i] = source[i];
69110
})
70-
.add("BufferWriter#bytes", function() {
71-
var writer = new protobuf.BufferWriter();
72-
writer.bytes(buffer);
73-
writer._finish(writer.head, bufferPlus1);
111+
.add("set buffer", function() {
112+
buffer.set(source, 0);
113+
})
114+
.add("for buffer", function() {
115+
for (var i = 0; i < source.length; ++i)
116+
buffer[i] = source[i];
117+
})
118+
.add("copy buffer", function() {
119+
source.copy(buffer, 0);
74120
})
75121
.run();
122+
*/
123+
124+
function writeString(buf, pos, val) {
125+
for (var i = 0; i < val.length; ++i) {
126+
var c1 = val.charCodeAt(i), c2;
127+
if (c1 < 128) {
128+
buf[pos++] = c1;
129+
} else if (c1 < 2048) {
130+
buf[pos++] = c1 >> 6 | 192;
131+
buf[pos++] = c1 & 63 | 128;
132+
} else if ((c1 & 0xFC00) === 0xD800 && ((c2 = val.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) {
133+
c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF);
134+
++i;
135+
buf[pos++] = c1 >> 18 | 240;
136+
buf[pos++] = c1 >> 12 & 63 | 128;
137+
buf[pos++] = c1 >> 6 & 63 | 128;
138+
buf[pos++] = c1 & 63 | 128;
139+
} else {
140+
buf[pos++] = c1 >> 12 | 224;
141+
buf[pos++] = c1 >> 6 & 63 | 128;
142+
buf[pos++] = c1 & 63 | 128;
143+
}
144+
}
145+
}
146+
147+
function byteLength(val) {
148+
var strlen = val.length >>> 0;
149+
var len = 0;
150+
for (var i = 0; i < strlen; ++i) {
151+
var c1 = val.charCodeAt(i);
152+
if (c1 < 128)
153+
len += 1;
154+
else if (c1 < 2048)
155+
len += 2;
156+
else if ((c1 & 0xFC00) === 0xD800 && (val.charCodeAt(i + 1) & 0xFC00) === 0xDC00) {
157+
++i;
158+
len += 4;
159+
} else
160+
len += 3;
161+
}
162+
return len;
163+
}
164+
165+
var array = new Uint8Array(1000);
166+
var buffer = new Buffer(1000);
167+
168+
[
169+
"Lorem ipsu",
170+
"Lorem ipsum dolo",
171+
"Lorem ipsum dolor ",
172+
"Lorem ipsum dolor s",
173+
"Lorem ipsum dolor si",
174+
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore ipsum."
175+
].forEach(function(str) {
176+
// raw string write speed
177+
newSuite("string[" + str.length + "]")
178+
.add("writeString array", function() {
179+
writeString(array, 0, str);
180+
})
181+
.add("writeString buffer", function() {
182+
writeString(buffer, 0, str)
183+
})
184+
.add("write buffer", function() {
185+
buffer.write(str, 0)
186+
})
187+
.add("utf8Write buffer", function() {
188+
buffer.utf8Write(str, 0)
189+
})
190+
/* .add("byteLength array", function() {
191+
byteLength(str)
192+
})
193+
.add("byteLength buffer", function() {
194+
Buffer.byteLength(str)
195+
}) */
196+
.run();
197+
});

0 commit comments

Comments
 (0)