Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

~30-40% perf win using 'charCodeAt' in CParser.write() #57

Merged
merged 8 commits into from
Jan 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
node_modules/
*.log
/.vscode
48 changes: 48 additions & 0 deletions benchmark/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable no-console */
const { Suite } = require("benchmark");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the benchmark I used to measure the impact of the change. It measures the four .json files under "../samples" and for me shows ~30+% speedup under 'node 8.11.4'. The speedup was significant enough that I didn't bother disabling turbo boost, sleep states, etc. on the CPU.

const { Listener } = require("./listener");
const oldClarinet = require("clarinet");
const newClarinet = require("..");

const oldParser = oldClarinet.parser();
const oldListener = new Listener(oldParser);

const newParser = newClarinet.parser();
const newListener = new Listener(newParser);

const suites =
["creationix", "npm", "twitter", "wikipedia"]
.map((name) => {
// eslint-disable-next-line security/detect-non-literal-require
return { name, json: JSON.stringify(require(`../samples/${name}.json`)) };
});

for (const { name, json } of suites) {
new Suite("name")
// Uncomment the below to add node's native JSON parsing to the results:
// .add(`native-${name}`, () => JSON.parse(json))
.add(`old-${name}`, () => {
oldListener.reset();
oldParser.write(json);
oldParser.close();
oldListener.check();
})
.add(`new-${name}`, () => {
newListener.reset();
newParser.write(json);
newParser.close();
newListener.check();
})
.on("cycle", (event) => {
console.log(String(event.target));
})
.on("error", (event) => {
console.error(String(event.target.error));
})
.on("complete", (event) => {
console.log(
`Fastest is ${event.currentTarget.filter("fastest").map("name")}\n`
);
})
.run();
}
74 changes: 74 additions & 0 deletions benchmark/listener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const assert = require("assert");

/**
* Listener that counts all events emitted by the Clarinet.js parser and sanity checks the totals.
* This defeats potential dead code elimination and helps make the benchmark more realistic.
*/
class Listener {
constructor(parser) {
this.reset();

parser.onready = () => {
this.ready++;
};

parser.onopenobject = (name) => {
this.openObject++;
typeof name === "undefined" || parser.onkey(name);
};

parser.onkey = (name) => {
this.key++;
assert(name !== "𝓥𝓸𝓵𝓭𝓮𝓶𝓸𝓻𝓽");
};

parser.oncloseobject = () => {
this.closeObject++;
};

parser.onopenarray = () => {
this.openArray++;
};

parser.onclosearray = () => {
this.closeArray++;
};

parser.onvalue = () => {
this.value++;
};

parser.onerror = () => {
this.error++;
};

parser.onend = () => {
this.end++;
};
}

/** Resets the counts between iterations. */
reset() {
this.ready = 0;
this.openObject = 0;
this.key = 0;
this.closeObject = 0;
this.openArray = 0;
this.closeArray = 0;
this.value = 0;
this.error = 0;
this.end = 0;
}

/** Sanity checks the total event counts. */
check() {
assert(this.ready === 1);
assert(this.end === 1);
assert(this.error === 0);
assert(this.value + this.openObject + this.openArray >= this.key);
assert(this.openObject === this.closeObject);
assert(this.openArray === this.closeArray);
}
}

module.exports = { Listener };
26 changes: 26 additions & 0 deletions benchmark/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "clarinet-benchmark",
"description": "Benchmark to measure clarinet.js vs. the last published version",
"version": "0.1.0",
"main": "./index.js",
"homepage": "https://github.com/dscape/clarinet",
"private": true,
"repository": {
"type": "git",
"url": "http://github.com/dscape/clarinet.git"
},
"bugs": {
"url": "http://github.com/dscape/clarinet/issues"
},
"devDependencies": {
"benchmark": "^2.1.4",
"mocha": "1.3.x"
},
"scripts": {
"test": "node index.js"
},
"license": "BSD-2-Clause",
"dependencies": {
"clarinet": "^0.12.1"
}
}
Loading