Skip to content

Commit 2cc2411

Browse files
committed
Benchmark task
This adds a benchmark task inspired by #1167 and #1163. Invoke with `yarn benchmark`. Allows filtering down which benchmark to run, ala jest, and supplying which revisions to run against.
1 parent 86d33b4 commit 2cc2411

File tree

5 files changed

+178
-0
lines changed

5 files changed

+178
-0
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ coverage
1616
resources
1717
src
1818
dist
19+
__tests__
1920
npm

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"testonly:cover": "babel-node ./node_modules/.bin/isparta cover --root src --report html _mocha -- $npm_package_options_mocha",
2626
"testonly:coveralls": "babel-node ./node_modules/.bin/isparta cover --root src --report lcovonly _mocha -- $npm_package_options_mocha && cat ./coverage/lcov.info | coveralls",
2727
"lint": "eslint --rulesdir ./resources/lint src || (printf '\\033[33mTry: \\033[7m npm run lint -- --fix \\033[0m\\n' && exit 1)",
28+
"benchmark": "node ./resources/benchmark.js",
2829
"prettier": "prettier --write 'src/**/*.js'",
2930
"check": "flow check",
3031
"check-cover": "for file in {src/*.js,src/**/*.js}; do echo $file; flow coverage $file; done",
@@ -52,6 +53,8 @@
5253
"babel-plugin-transform-flow-strip-types": "6.22.0",
5354
"babel-plugin-transform-object-rest-spread": "6.26.0",
5455
"babel-preset-env": "^1.5.2",
56+
"beautify-benchmark": "0.2.4",
57+
"benchmark": "2.1.4",
5558
"chai": "4.1.2",
5659
"chai-json-equal": "0.0.1",
5760
"chai-spies-next": "0.9.3",

resources/benchmark.js

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
const {Suite} = require('benchmark');
9+
const beautifyBenchmark = require('beautify-benchmark');
10+
const {execSync} = require('child_process');
11+
const os = require('os');
12+
const path = require('path');
13+
14+
// Like build:cjs, but includes __tests__ and copies other files.
15+
const BUILD_CMD = 'babel src --optional runtime --copy-files --out-dir dist/';
16+
const LOCAL = 'local';
17+
const LOCAL_DIR = path.join(__dirname, '../');
18+
const TEMP_DIR = os.tmpdir();
19+
20+
// Get the revisions and make things happen!
21+
const {benchmarkPatterns, revisions} = getArguments(process.argv.slice(2));
22+
prepareAndRunBenchmarks(benchmarkPatterns, revisions);
23+
24+
// Returns the complete git hash for a given git revision reference.
25+
function hashForRevision(revision) {
26+
if (revision === LOCAL) {
27+
return revision;
28+
}
29+
const out = execSync(`git rev-parse "${revision}"`, {encoding: 'utf8'});
30+
const match = /[0-9a-f]{8,40}/.exec(out);
31+
if (!match) {
32+
throw new Error(`Bad results for revision ${revision}: ${out}`);
33+
}
34+
return match[0];
35+
}
36+
37+
// Returns the temporary directory which hosts the files for this git hash.
38+
function dirForHash(hash) {
39+
if (hash === LOCAL) {
40+
return path.join(__dirname, '../');
41+
}
42+
return path.join(TEMP_DIR, 'graphql-js-benchmark', hash);
43+
}
44+
45+
// Build a benchmarkable environment for the given revision.
46+
function prepareRevision(revision) {
47+
const hash = hashForRevision(revision);
48+
const dir = dirForHash(hash);
49+
if (hash === LOCAL) {
50+
execSync(`(cd "${dir}" && yarn run ${BUILD_CMD})`);
51+
} else {
52+
execSync(`
53+
if [ ! -d "${dir}" ]; then
54+
mkdir -p "${dir}" &&
55+
git archive "${hash}" | tar -xC "${dir}" &&
56+
(cd "${dir}" && yarn install);
57+
fi &&
58+
# Copy in local tests so the same logic applies to each revision.
59+
for file in $(cd "${LOCAL_DIR}src"; find . -path '*/__tests__/*.js');
60+
do cp "${LOCAL_DIR}src/$file" "${dir}/src/$file";
61+
done &&
62+
(cd "${dir}" && yarn run ${BUILD_CMD})
63+
`);
64+
}
65+
}
66+
67+
// Find all benchmark tests to be run.
68+
function findBenchmarks() {
69+
const out = execSync(
70+
`(cd ${LOCAL_DIR}src; find . -path '*/__tests__/*-benchmark.js')`,
71+
{encoding: 'utf8'}
72+
);
73+
return out.split('\n').filter(Boolean);
74+
}
75+
76+
// Run a given benchmark test with the provided revisions.
77+
function runBenchmark(benchmark, revisions) {
78+
const modules = revisions.map(revision =>
79+
require(path.join(dirForHash(hashForRevision(revision)), 'dist', benchmark))
80+
);
81+
const suite = new Suite(modules[0].name, {
82+
onStart(event) {
83+
console.log('⏱️ ' + event.currentTarget.name);
84+
},
85+
onCycle(event) {
86+
beautifyBenchmark.add(event.target);
87+
},
88+
onComplete() {
89+
beautifyBenchmark.log();
90+
},
91+
});
92+
for (let i = 0; i < revisions.length; i++) {
93+
suite.add(revisions[i], modules[i].measure);
94+
}
95+
suite.run();
96+
}
97+
98+
// Prepare all revisions and run benchmarks matching a pattern against them.
99+
function prepareAndRunBenchmarks(benchmarkPatterns, revisions) {
100+
const benchmarks = findBenchmarks().filter(benchmark =>
101+
benchmarkPatterns.length === 0 ||
102+
benchmarkPatterns.some(pattern => benchmark.indexOf(pattern) !== -1)
103+
);
104+
if (benchmarks.length === 0) {
105+
console.warn(`No benchmarks matching: \u001b[1m${benchmarkPatterns.join('\u001b[0m or \u001b[1m')}\u001b[0m`);
106+
return;
107+
}
108+
revisions.forEach(revision => {
109+
console.log(`🍳 Preparing ${revision}...`);
110+
prepareRevision(revision);
111+
});
112+
benchmarks.forEach(benchmark =>
113+
runBenchmark(benchmark, revisions)
114+
);
115+
}
116+
117+
function getArguments(argv) {
118+
const revsIdx = argv.indexOf('--revs');
119+
const revsArgs = revsIdx === -1 ? [] : argv.slice(revsIdx + 1);
120+
const benchmarkPatterns = revsIdx === -1 ? argv : argv.slice(0, revsIdx);
121+
let assumeArgs;
122+
let revisions;
123+
switch (revsArgs.length) {
124+
case 0:
125+
assumeArgs = [...benchmarkPatterns, '--revs', 'local', 'HEAD'];
126+
revisions = [LOCAL, 'HEAD']
127+
break;
128+
case 1:
129+
assumeArgs = [...benchmarkPatterns, '--revs', 'local', revsArgs[0]];
130+
revisions = [LOCAL, revsArgs[0]]
131+
break;
132+
default:
133+
revisions = revsArgs;
134+
break;
135+
}
136+
if (assumeArgs) {
137+
console.warn(`Assuming you meant: \u001b[1mbenchmark ${assumeArgs.join(' ')}\u001b[0m`);
138+
}
139+
return {benchmarkPatterns, revisions};
140+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import { join } from 'path';
9+
import { readFileSync } from 'fs';
10+
import { parse } from '../parser';
11+
12+
const kitchenSink = readFileSync(join(__dirname, '/kitchen-sink.graphql'), {
13+
encoding: 'utf8',
14+
});
15+
16+
export const name = 'Parse kitchen sink';
17+
export function measure() {
18+
parse(kitchenSink);
19+
}

yarn.lock

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,17 @@ bcrypt-pbkdf@^1.0.0:
786786
dependencies:
787787
tweetnacl "^0.14.3"
788788

789+
790+
version "0.2.4"
791+
resolved "https://registry.yarnpkg.com/beautify-benchmark/-/beautify-benchmark-0.2.4.tgz#3151def14c1a2e0d07ff2e476861c7ed0e1ae39b"
792+
793+
794+
version "2.1.4"
795+
resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629"
796+
dependencies:
797+
lodash "^4.17.4"
798+
platform "^1.3.3"
799+
789800
binary-extensions@^1.0.0:
790801
version "1.11.0"
791802
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
@@ -2240,6 +2251,10 @@ pinkie@^2.0.0:
22402251
version "2.0.4"
22412252
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
22422253

2254+
platform@^1.3.3:
2255+
version "1.3.5"
2256+
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444"
2257+
22432258
pluralize@^7.0.0:
22442259
version "7.0.0"
22452260
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"

0 commit comments

Comments
 (0)