Skip to content

Commit 9329a92

Browse files
committed
Add MVP of mocha-like benchmark tests
Based on #1163 by @mohawk2
1 parent 358df97 commit 9329a92

File tree

9 files changed

+372
-10
lines changed

9 files changed

+372
-10
lines changed

.flowconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.*/dist/.*
44
.*/coverage/.*
55
.*/resources/.*
6+
.*/benchmark/.*
67

78
[include]
89

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ node_modules
99
coverage
1010
dist
1111
npm
12+
benchmark

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ resources
1717
src
1818
dist
1919
npm
20+
benchmark

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"prettier": "prettier --write 'src/**/*.js'",
3030
"check": "flow check",
3131
"check-cover": "for file in {src/*.js,src/**/*.js}; do echo $file; flow coverage $file; done",
32+
"benchmark": "node resources/benchmark.js",
3233
"build": "npm run build:clean && npm run build:npm && npm run build:npm-flow && npm run build:module && npm run build:module-flow && npm run build:package-json",
3334
"build:clean": "rm -rf ./dist",
3435
"build:package-json": "node ./resources/copy-package-json.js",
@@ -56,19 +57,24 @@
5657
"babel-plugin-transform-flow-strip-types": "6.22.0",
5758
"babel-plugin-transform-object-rest-spread": "6.23.0",
5859
"babel-preset-env": "^1.5.2",
60+
"beautify-benchmark": "^0.2.4",
61+
"benchmark": "^2.1.4",
5962
"chai": "4.1.1",
6063
"chai-json-equal": "0.0.1",
6164
"chai-spies-next": "^0.8.0",
6265
"chai-subset": "1.5.0",
66+
"chalk": "^2.3.0",
6367
"coveralls": "2.13.1",
6468
"eslint": "4.4.1",
6569
"eslint-plugin-babel": "4.1.2",
6670
"eslint-plugin-flowtype": "2.35.0",
6771
"eslint-plugin-prettier": "^2.3.1",
6872
"flow-bin": "0.61.0",
6973
"isparta": "4.0.0",
74+
"microtime": "^2.1.7",
7075
"mocha": "3.5.0",
7176
"prettier": "^1.9.2",
72-
"sane": "2.0.0"
77+
"sane": "2.0.0",
78+
"shelljs": "^0.7.8"
7379
}
7480
}

resources/benchmark.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const benchmark = require('benchmark');
2+
const beautifyBenchmark = require('beautify-benchmark');
3+
const sh = require('shelljs');
4+
const chalk = require('chalk');
5+
const pathJoin = require('path').join;
6+
7+
const args = process.argv.slice(2);
8+
args[0] = args[0] || 'HEAD';
9+
args[1] = args[1] || 'local';
10+
11+
console.log('Benchmarking revisions: ' + args.join(', '));
12+
13+
const localDistDir = './benchmark/local';
14+
sh.rm('-rf', localDistDir);
15+
console.log(`Building local dist: ${localDistDir}`);
16+
sh.mkdir('-p', localDistDir);
17+
exec(`babel src --optional runtime --copy-files --out-dir ${localDistDir}`);
18+
19+
const revisions = {};
20+
for (const arg of args) {
21+
const distPath = buildRevisionDist(arg);
22+
const distRequire = (path) => reqireFromCWD(pathJoin(distPath, path));
23+
revisions[arg] = distRequire;
24+
}
25+
26+
const suites = {};
27+
global.suite = suite;
28+
const testFiles = sh.ls(`${localDistDir}/**/__tests__/**/*-benchmark.js`);
29+
for (const file of testFiles) {
30+
reqireFromCWD(file);
31+
}
32+
33+
dummyRun();
34+
for (const [name, measures] of Object.entries(suites)) {
35+
console.log(chalk.green(name) + '\n');
36+
benchmark.invoke(measures, 'run');
37+
}
38+
39+
function reqireFromCWD(path) {
40+
return require(pathJoin(process.cwd(), path))
41+
}
42+
43+
function dummyRun() {
44+
benchmark.Suite()
45+
.add('dummy', () => { Math.pow(2, 256); })
46+
.run();
47+
}
48+
49+
function newMeasurement(name) {
50+
return new benchmark.Suite(name, {
51+
onStart(event) {
52+
console.log(' ⏱️ ', event.currentTarget.name);
53+
},
54+
onCycle(event) {
55+
beautifyBenchmark.add(event.target);
56+
},
57+
onComplete() {
58+
beautifyBenchmark.log();
59+
},
60+
});
61+
}
62+
63+
function suite(name, fn) {
64+
const measures = {};
65+
for (const [revision, distRequire] of Object.entries(revisions)) {
66+
currentRevision = revision;
67+
global.measure = (name, fn) => {
68+
measures[name] = measures[name] || newMeasurement(name);
69+
measures[name].add(revision, fn);
70+
};
71+
try {
72+
fn(distRequire);
73+
} catch (e) {
74+
console.error(e.stack);
75+
}
76+
}
77+
global.measure = undefined;
78+
suites[name] = Object.values(measures);
79+
}
80+
81+
function exec(command) {
82+
const {code, stdout, stderr} = sh.exec(command, {silent: true});
83+
if (code !== 0) {
84+
console.error(stdout);
85+
console.error(stderr);
86+
sh.exit(code);
87+
}
88+
return stdout.trim();
89+
}
90+
91+
function buildRevisionDist(revision) {
92+
if (revision === 'local') {
93+
return localDistDir;
94+
}
95+
96+
const hash = exec(`git log -1 --format=%h "${revision}"`);
97+
const buildDir = './benchmark/' + hash;
98+
const distDir = buildDir + '/dist'
99+
100+
if (sh.test('-d', buildDir)) {
101+
return distDir;
102+
}
103+
console.log(`Building "${revision}"(${hash}) revision: ${buildDir}`);
104+
sh.mkdir('-p', buildDir);
105+
exec(`git archive "${hash}" | tar -xC "${buildDir}"`);
106+
107+
const pwd = sh.pwd();
108+
sh.cd(buildDir);
109+
exec('yarn && npm run build');
110+
sh.cd(pwd);
111+
return buildDir + '/dist';
112+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 { readFileSync } from 'fs';
9+
import { join } from 'path';
10+
import { getIntrospectionQuery } from '../../utilities/introspectionQuery';
11+
/* global suite, measure */
12+
13+
suite('Run lexer on a string', distRequire => {
14+
const { Source } = distRequire('language/source');
15+
const { createLexer } = distRequire('language/lexer');
16+
17+
function runLexer(source) {
18+
try {
19+
const lexer = createLexer(source);
20+
let token;
21+
do {
22+
token = lexer.advance();
23+
} while (token.kind !== '<EOF>');
24+
} catch (e) {
25+
console.error(e.stack);
26+
}
27+
}
28+
29+
const kitchenSinkPath = join(__dirname, './kitchen-sink.graphql');
30+
const kitchenSink = new Source(
31+
readFileSync(kitchenSinkPath, { encoding: 'utf8' }),
32+
);
33+
34+
measure('Kitchen Sink', () => runLexer(kitchenSink));
35+
36+
const introspectionQuery = new Source(getIntrospectionQuery());
37+
measure('Introspection Query', () => runLexer(introspectionQuery));
38+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 { readFileSync } from 'fs';
9+
import { join } from 'path';
10+
import { getIntrospectionQuery } from '../../utilities/introspectionQuery';
11+
/* global suite, measure */
12+
13+
suite('Parse string to AST', distRequire => {
14+
const { parse } = distRequire('language/parser');
15+
16+
const kitchenSink = readFileSync(join(__dirname, './kitchen-sink.graphql'), {
17+
encoding: 'utf8',
18+
});
19+
20+
measure('Kitchen Sink', () => parse(kitchenSink));
21+
22+
const introspectionQuery = getIntrospectionQuery();
23+
measure('Introspection Query', () => parse(introspectionQuery));
24+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 { readFileSync } from 'fs';
9+
import { join } from 'path';
10+
import { getIntrospectionQuery } from '../../utilities/introspectionQuery';
11+
/* global suite, measure */
12+
13+
suite('Print AST', distRequire => {
14+
const { print } = distRequire('language/printer');
15+
const { parse } = distRequire('language/parser');
16+
17+
const kitchenSink = readFileSync(join(__dirname, './kitchen-sink.graphql'), {
18+
encoding: 'utf8',
19+
});
20+
const kitchenSinkAST = parse(kitchenSink);
21+
measure('Kitchen Sink', () => print(kitchenSinkAST));
22+
23+
const introspectionQueryAST = parse(getIntrospectionQuery());
24+
measure('Introspection Query', () => print(introspectionQueryAST));
25+
});

0 commit comments

Comments
 (0)