Skip to content

Commit 5f3c2b5

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 5f3c2b5

File tree

5 files changed

+180
-0
lines changed

5 files changed

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