Skip to content

Commit c1bfc51

Browse files
committed
feat(@ngtools/benchmark): add benchmark package
Adds `@ngtools/benchmark` allowing the benchmark of commands via `ngbench` binary.
1 parent 0dc2200 commit c1bfc51

File tree

13 files changed

+500
-26
lines changed

13 files changed

+500
-26
lines changed

.travis.yml

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,35 @@ matrix:
1414
- node_js: "7"
1515
- env: NODE_SCRIPT="tests/run_e2e.js --nightly"
1616
include:
17+
# - node_js: "6"
18+
# os: linux
19+
# env: SCRIPT=lint
20+
# - node_js: "6"
21+
# os: linux
22+
# env: SCRIPT=build
23+
# - node_js: "6"
24+
# os: linux
25+
# env: SCRIPT=test
26+
# - node_js: "6"
27+
# os: linux
28+
# env: NODE_SCRIPT=tests/run_e2e.js
1729
- node_js: "6"
1830
os: linux
19-
env: SCRIPT=lint
20-
- node_js: "6"
21-
os: linux
22-
env: SCRIPT=build
23-
- node_js: "6"
24-
os: linux
25-
env: SCRIPT=test
26-
- node_js: "6"
27-
os: linux
28-
env: NODE_SCRIPT=tests/run_e2e.js
29-
- node_js: "6"
30-
os: osx
31-
env: NODE_SCRIPT=tests/run_e2e.js
31+
env: NODE_SCRIPT="tests/run_e2e.js --benchmark"
32+
# - node_js: "6"
33+
# os: osx
34+
# env: NODE_SCRIPT=tests/run_e2e.js
3235

33-
# Optional builds.
34-
- node_js: "6"
35-
os: osx
36-
env: SCRIPT=test
37-
- node_js: "6"
38-
os: linux
39-
env: NODE_SCRIPT="tests/run_e2e.js --nightly"
40-
- node_js: "7"
41-
os: linux
42-
env: NODE_SCRIPT=tests/run_e2e.js
36+
# # Optional builds.
37+
# - node_js: "6"
38+
# os: osx
39+
# env: SCRIPT=test
40+
# - node_js: "6"
41+
# os: linux
42+
# env: NODE_SCRIPT="tests/run_e2e.js --nightly"
43+
# - node_js: "7"
44+
# os: linux
45+
# env: NODE_SCRIPT=tests/run_e2e.js
4346

4447
before_install:
4548
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi

bin/ngbench

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env node
2+
'use strict';
3+
4+
// Provide a title to the process in `ps`
5+
process.title = '@ngtools/benchmark';
6+
7+
require('../lib/bootstrap-local');
8+
require('../packages/@ngtools/benchmark/bin/ngbench');

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "packages/@angular/cli/lib/cli/index.js",
66
"trackingCode": "UA-8594346-19",
77
"bin": {
8-
"ng": "./bin/ng"
8+
"ng": "./bin/ng",
9+
"ngbench": "./bin/ngbench"
910
},
1011
"keywords": [],
1112
"scripts": {
@@ -96,18 +97,21 @@
9697
"source-map": "^0.5.6",
9798
"source-map-loader": "^0.1.5",
9899
"sourcemap-istanbul-instrumenter-loader": "^0.2.0",
100+
"strip-ansi": "^3.0.1",
99101
"style-loader": "^0.13.1",
100102
"stylus": "^0.54.5",
101103
"stylus-loader": "^2.4.0",
102104
"temp": "0.8.3",
103105
"through": "^2.3.6",
106+
"tree-kill": "^1.1.0",
104107
"typescript": "~2.0.3",
105108
"url-loader": "^0.5.7",
106109
"walk-sync": "^0.2.6",
107110
"webpack": "2.2.0",
108111
"webpack-dev-server": "2.2.0-rc.0",
109112
"webpack-merge": "^2.4.0",
110113
"webpack-sources": "^0.1.3",
114+
"yargs": "^6.6.0",
111115
"zone.js": "^0.7.2"
112116
},
113117
"ember-addon": {
@@ -132,6 +136,7 @@
132136
"@types/semver": "^5.3.30",
133137
"@types/source-map": "^0.5.0",
134138
"@types/webpack": "^2.2.4",
139+
"@types/yargs": "^6.5.0",
135140
"chai": "^3.5.0",
136141
"conventional-changelog": "^1.1.0",
137142
"dtsgenerator": "^0.7.1",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
require('../src/index');
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@ngtools/benchmark",
3+
"version": "0.1.0",
4+
"description": "",
5+
"main": "./src/index.js",
6+
"bin": {
7+
"ngbench": "./bin/ngbench"
8+
},
9+
"license": "MIT",
10+
"dependencies": {
11+
"strip-ansi": "^3.0.1",
12+
"tree-kill": "^1.1.0",
13+
"yargs": "^6.6.0"
14+
}
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface BenchmarkOptions {
2+
command: string;
3+
iterations: number;
4+
extraArgs: string[];
5+
match: string;
6+
matchCount: number;
7+
matchEditFile: string;
8+
matchEditString: string;
9+
comment: string;
10+
logFile: string;
11+
debug: boolean;
12+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import * as fs from 'fs';
2+
3+
import { BenchmarkOptions } from './benchmark-options';
4+
import {
5+
benchmarkLog,
6+
isNumber,
7+
average,
8+
combine,
9+
makeArray,
10+
makeMatchFn,
11+
matchSpawn,
12+
serialMultiPromise
13+
} from './utils';
14+
15+
export const benchmark = (benchmarkOptions: BenchmarkOptions) => {
16+
const log = benchmarkLog.bind(null, benchmarkOptions);
17+
const logIfDefined = (str: string, prop: any) => {
18+
if (Array.isArray(prop) && prop.length === 0) {
19+
prop = null;
20+
}
21+
return prop ? log(str + prop) : null;
22+
};
23+
24+
const cwd = process.cwd();
25+
26+
// Build match function
27+
let matchFn: any = null;
28+
if (benchmarkOptions.match) {
29+
matchFn = makeMatchFn(
30+
benchmarkOptions.debug,
31+
benchmarkOptions.match,
32+
benchmarkOptions.matchCount,
33+
benchmarkOptions.matchEditFile,
34+
benchmarkOptions.matchEditString,
35+
);
36+
}
37+
38+
let editedFileContents: string;
39+
if (benchmarkOptions.matchEditFile) {
40+
// backup contents of file that is being edited for rebuilds
41+
editedFileContents = fs.readFileSync(benchmarkOptions.matchEditFile, 'utf8');
42+
}
43+
// combine flags commands
44+
let flagCombinations = combine(benchmarkOptions.extraArgs);
45+
flagCombinations.unshift([]);
46+
47+
const startTime = Date.now();
48+
log(`Base command: ${benchmarkOptions.command}`);
49+
log(`Iterations: ${benchmarkOptions.iterations}`);
50+
logIfDefined('Comment: ', benchmarkOptions.comment);
51+
logIfDefined('Extra args: ', benchmarkOptions.extraArgs);
52+
logIfDefined('Logging to: ', benchmarkOptions.logFile);
53+
if (benchmarkOptions.match) {
54+
log(`Match output: ${benchmarkOptions.match}`);
55+
logIfDefined('Match count: ', benchmarkOptions.matchCount);
56+
logIfDefined('Match edit file: ', benchmarkOptions.matchEditFile);
57+
logIfDefined('Match edit string: ', benchmarkOptions.matchEditString);
58+
}
59+
if (benchmarkOptions.debug) {
60+
log('### Debug mode, all output is logged ###');
61+
}
62+
log('');
63+
64+
65+
let promise = Promise.resolve();
66+
let hasFailures = false;
67+
68+
flagCombinations.forEach((flags) =>
69+
promise = promise
70+
.then(() => serialMultiPromise(
71+
benchmarkOptions.iterations,
72+
matchSpawn,
73+
benchmarkOptions.debug,
74+
cwd,
75+
matchFn,
76+
benchmarkOptions.command,
77+
flags
78+
).then((results: any[]) => {
79+
const failures = results.filter(result => result.err && result.err !== 0);
80+
log(`Full command: ${benchmarkOptions.command} ${flags.join(' ')}`);
81+
82+
let times = results.filter(result => !result.err)
83+
.map((result) => result.time);
84+
log(`Time average: ${average(times)}`);
85+
log(`Times: ${times.join()}`);
86+
87+
if (benchmarkOptions.match) {
88+
let matches = results.filter(result => !result.err)
89+
.map((result) => result.match);
90+
if (matches.every(match => isNumber(match))) {
91+
log(`Match average: ${average(matches)}`);
92+
}
93+
log(`Matches: ${matches.join()}`);
94+
}
95+
96+
if (failures.length > 0) {
97+
hasFailures = true;
98+
log(`Failures: ${failures.length}`);
99+
log(failures);
100+
}
101+
log('');
102+
}))
103+
);
104+
105+
return promise.then(() => {
106+
log(`Benchmark execution time: ${Date.now() - startTime}ms`);
107+
// restore contents of file that was being edited for rebuilds
108+
if (benchmarkOptions.matchEditFile) {
109+
fs.writeFileSync(benchmarkOptions.matchEditFile, editedFileContents, 'utf8');
110+
}
111+
return hasFailures ? Promise.reject(new Error('Some benchmarks failed')) : Promise.resolve();
112+
});
113+
};
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as yargs from 'yargs';
4+
5+
import { BenchmarkOptions } from './benchmark-options';
6+
import { benchmarkLog } from './utils';
7+
import { benchmark } from './benchmark';
8+
9+
10+
// Set options via yargs
11+
const benchmarkOptions: BenchmarkOptions = yargs
12+
.usage('$0 [args]')
13+
.options({
14+
'command': {
15+
description: 'Command to benchmark, defaults to benchmarking `ng serve` if not set',
16+
type: 'string',
17+
alias: 'c'
18+
},
19+
'iterations': {
20+
description: 'Number of iterations to run benchmark',
21+
type: 'number',
22+
default: 5,
23+
alias: 'i'
24+
},
25+
'extra-args': {
26+
description: 'Extra arguments to combine and run',
27+
type: 'array',
28+
default: [],
29+
alias: 'ea'
30+
},
31+
'match': {
32+
description: 'Command output to match',
33+
type: 'string',
34+
alias: 'm'
35+
},
36+
'match-count': {
37+
description: 'Times to match output',
38+
type: 'number',
39+
alias: 'mc'
40+
},
41+
'match-edit-file': {
42+
description: 'File to edit after a output match',
43+
type: 'string',
44+
alias: 'mef'
45+
},
46+
'match-edit-string': {
47+
description: 'String to use in --match-edit-file',
48+
type: 'string',
49+
alias: 'mes'
50+
},
51+
'comment': {
52+
description: 'Comment to add to output',
53+
type: 'string',
54+
alias: 'cm'
55+
},
56+
'log-file': {
57+
description: 'File to log output',
58+
type: 'string',
59+
alias: 'lf'
60+
},
61+
'debug': {
62+
description: 'Show command output',
63+
type: 'boolean',
64+
default: false,
65+
alias: 'd'
66+
},
67+
})
68+
.help()
69+
.argv;
70+
71+
// Set compound defauls and resolve paths
72+
if (!benchmarkOptions.command) {
73+
benchmarkOptions.command = 'ng serve --no-progress';
74+
benchmarkOptions.match = 'Time: (.*)ms';
75+
benchmarkOptions.matchCount = 4;
76+
benchmarkOptions.matchEditFile = 'src/main.ts';
77+
benchmarkOptions.matchEditString = 'console.log(1);';
78+
}
79+
80+
if (benchmarkOptions.matchEditFile) {
81+
benchmarkOptions.matchEditFile = path.resolve('./', benchmarkOptions.matchEditFile);
82+
}
83+
84+
if (benchmarkOptions.command.match(/^ng (build|serve)/)
85+
&& benchmarkOptions.command.match(/--progress/) !== undefined
86+
) {
87+
benchmarkLog(benchmarkOptions, 'Auto-added \'--no-progress\' to build/serve command.');
88+
benchmarkOptions.command = benchmarkOptions.command + ' --no-progress';
89+
}
90+
91+
// Run benchmark
92+
benchmark(benchmarkOptions)
93+
.catch((err) => {
94+
console.log(err);
95+
// process.exit(1);
96+
});

0 commit comments

Comments
 (0)