Skip to content

Commit

Permalink
plain js utf8 is faster for short strings
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodeIO committed Dec 3, 2016
1 parent 0ae6675 commit 98d6ae1
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 123 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,33 +372,33 @@ The package includes a [benchmark](https://github.com/dcodeIO/protobuf.js/tree/m
```
benchmarking encoding performance ...
Type.encode to buffer x 402,572 ops/sec ±1.09% (90 runs sampled)
JSON.stringify to string x 342,004 ops/sec ±1.46% (82 runs sampled)
JSON.stringify to buffer x 184,468 ops/sec ±1.76% (79 runs sampled)
Type.encode to buffer x 514,048 ops/sec ±0.75% (93 runs sampled)
JSON.stringify to string x 355,935 ops/sec ±0.79% (91 runs sampled)
JSON.stringify to buffer x 191,023 ops/sec ±1.39% (86 runs sampled)
Type.encode to buffer was fastest
JSON.stringify to string was 15.4% slower
JSON.stringify to buffer was 54.5% slower
JSON.stringify to string was 30.8% slower
JSON.stringify to buffer was 63.1% slower
benchmarking decoding performance ...
Type.decode from buffer x 1,170,490 ops/sec ±1.49% (88 runs sampled)
JSON.parse from string x 328,975 ops/sec ±0.90% (88 runs sampled)
JSON.parse from buffer x 298,702 ops/sec ±0.82% (89 runs sampled)
Type.decode from buffer x 1,238,587 ops/sec ±1.73% (87 runs sampled)
JSON.parse from string x 312,168 ops/sec ±2.22% (83 runs sampled)
JSON.parse from buffer x 272,975 ops/sec ±2.45% (82 runs sampled)
Type.decode from buffer was fastest
JSON.parse from string was 71.7% slower
JSON.parse from buffer was 74.3% slower
JSON.parse from string was 74.9% slower
JSON.parse from buffer was 78.1% slower
benchmarking combined performance ...
Type to/from buffer x 218,688 ops/sec ±1.49% (90 runs sampled)
JSON to/from string x 144,634 ops/sec ±1.97% (87 runs sampled)
JSON to/from buffer x 102,350 ops/sec ±1.23% (92 runs sampled)
Type to/from buffer x 246,428 ops/sec ±1.52% (89 runs sampled)
JSON to/from string x 136,380 ops/sec ±1.50% (80 runs sampled)
JSON to/from buffer x 95,229 ops/sec ±1.93% (86 runs sampled)
Type to/from buffer was fastest
JSON to/from string was 34.2% slower
JSON to/from buffer was 53.1% slower
JSON to/from string was 44.6% slower
JSON to/from buffer was 61.5% slower
```

Note that JSON is a native binding nowadays and as such is *really* fast. So, how can protobuf.js be faster?
Expand Down
50 changes: 2 additions & 48 deletions bench/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
var benchmark = require("benchmark"),
chalk = require("chalk");

var protobuf = require("../src/index"),
suite = new benchmark.Suite(),
newSuite = require("./suite"),
data = require("./bench.json");

// NOTE: This benchmark is flawed in that it compares protocol buffers, which is purely a binary
Expand Down Expand Up @@ -68,47 +65,4 @@ protobuf.load(require.resolve("./bench.proto"), function onload(err, root) {
})
.run();

});

var padSize = 27;

function newSuite(name) {
var benches = [];
return new benchmark.Suite(name)
.on("add", function(event) {
benches.push(event.target);
})
.on("start", function() {
console.log("benchmarking " + name + " performance ...\n");
})
.on("error", function(err) {
console.log("ERROR:", err);
})
.on("cycle", function(event) {
console.log(String(event.target));
})
.on("complete", function(event) {
var fastest = this.filter('fastest'),
slowest = this.filter('slowest');
var fastestHz = getHz(fastest[0]);
console.log("\n" + chalk.white(pad(fastest[0].name, padSize)) + " was " + chalk.green("fastest"));
benches.forEach(function(bench) {
if (fastest.indexOf(bench) > -1)
return;
var hz = hz = getHz(bench);
var percent = (1 - (hz / fastestHz)) * 100;
console.log(chalk.white(pad(bench.name, padSize)) + " was " + chalk.red(percent.toFixed(1)+'% slower'));
});
console.log();
});
}

function getHz(bench) {
return 1 / (bench.stats.mean + bench.stats.moe);
}

function pad(str, len, l) {
while (str.length < len)
str = l ? str + " " : " " + str;
return str;
}
});
45 changes: 45 additions & 0 deletions bench/suite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var benchmark = require("benchmark"),
chalk = require("chalk");

var padSize = 27;

module.exports = function newSuite(name) {
var benches = [];
return new benchmark.Suite(name)
.on("add", function(event) {
benches.push(event.target);
})
.on("start", function() {
console.log("benchmarking " + name + " performance ...\n");
})
.on("error", function(err) {
console.log("ERROR:", err);
})
.on("cycle", function(event) {
console.log(String(event.target));
})
.on("complete", function(event) {
var fastest = this.filter('fastest'),
slowest = this.filter('slowest');
var fastestHz = getHz(fastest[0]);
console.log("\n" + chalk.white(pad(fastest[0].name, padSize)) + " was " + chalk.green("fastest"));
benches.forEach(function(bench) {
if (fastest.indexOf(bench) > -1)
return;
var hz = hz = getHz(bench);
var percent = (1 - (hz / fastestHz)) * 100;
console.log(chalk.white(pad(bench.name, padSize)) + " was " + chalk.red(percent.toFixed(1)+'% slower'));
});
console.log();
});
}

function getHz(bench) {
return 1 / (bench.stats.mean + bench.stats.moe);
}

function pad(str, len, l) {
while (str.length < len)
str = l ? str + " " : " " + str;
return str;
}
45 changes: 45 additions & 0 deletions bench/write.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var protobuf = require("../src/index"),
newSuite = require("./suite");

newSuite("float")
.add("Writer#float", function() {
var writer = new protobuf.Writer();
writer.float(0.1);
writer.finish();
})
.add("BufferWriter#float", function() {
var writer = new protobuf.BufferWriter();
writer.float(0.1);
writer.finish();
})
.run();

newSuite("double")
.add("Writer#double", function() {
var writer = new protobuf.Writer();
writer.double(0.1);
writer.finish();
})
.add("BufferWriter#double", function() {
var writer = new protobuf.BufferWriter();
writer.double(0.1);
writer.finish();
})
.run();

var bytes = [0, 0, 0, 0, 0, 0, 0, 0];
var arrayBytes = new Uint8Array(bytes);
var bufferBytes = Buffer.from(bytes);

newSuite("bytes")
.add("Writer#bytes", function() {
var writer = new protobuf.Writer();
writer.bytes(arrayBytes);
writer.finish();
})
.add("BufferWriter#bytes", function() {
var writer = new protobuf.BufferWriter();
writer.bytes(bufferBytes);
writer.finish();
})
.run();
56 changes: 28 additions & 28 deletions dist/protobuf.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/protobuf.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/protobuf.min.js

Large diffs are not rendered by default.

Binary file modified dist/protobuf.min.js.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion dist/protobuf.min.js.map

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use strict";
var protobuf = global.protobuf = exports;

var util = require("./util");

/**
* Loads one or multiple .proto or preprocessed .json files into a common root namespace.
* @param {string|string[]} filename One or multiple files to load
Expand Down Expand Up @@ -54,7 +52,7 @@ protobuf.inherits = require("./inherits");
// Utility
protobuf.types = require("./types");
protobuf.common = require("./common");
protobuf.util = util;
protobuf.util = require("./util");

// Be nice to AMD
/* eslint-disable no-undef */
Expand Down
Loading

0 comments on commit 98d6ae1

Please sign in to comment.