Skip to content

Commit c262527

Browse files
filipesilvaKeen Yee Liau
authored and
Keen Yee Liau
committed
feat(@angular-devkit/benchmark): add package
1 parent 9fdcdf4 commit c262527

22 files changed

+1096
-0
lines changed

.monorepo.json

+5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@
8787
"hash": "51b6cc55b25fce87f6a92f0d0942f79c",
8888
"snapshotRepo": "angular/angular-devkit-architect-cli-builds"
8989
},
90+
"@angular-devkit/benchmark": {
91+
"name": "Benchmark",
92+
"section": "Tooling",
93+
"version": "0.0.0"
94+
},
9095
"@angular-devkit/build-optimizer": {
9196
"name": "Build Optimizer",
9297
"links": [

bin/benchmark

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env node
2+
/**
3+
* @license
4+
* Copyright Google Inc. All Rights Reserved.
5+
*
6+
* Use of this source code is governed by an MIT-style license that can be
7+
* found in the LICENSE file at https://angular.io/license
8+
*/
9+
'use strict';
10+
11+
12+
require('../lib/bootstrap-local');
13+
const packages = require('../lib/packages').packages;
14+
const main = require(packages['@angular-devkit/benchmark'].bin['benchmark']).main;
15+
16+
const args = process.argv.slice(2);
17+
main({ args })
18+
.then(exitCode => process.exitCode = exitCode)
19+
.catch(e => { throw (e); });

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"description": "Software Development Kit for Angular",
66
"bin": {
77
"architect": "./bin/architect",
8+
"benchmark": "./bin/benchmark",
89
"build-optimizer": "./bin/build-optimizer",
910
"devkit-admin": "./bin/devkit-admin",
1011
"ng": "./bin/ng",
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright Google Inc. All Rights Reserved.
2+
#
3+
# Use of this source code is governed by an MIT-style license that can be
4+
# found in the LICENSE file at https://angular.io/license
5+
6+
licenses(["notice"]) # MIT
7+
8+
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
9+
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test")
10+
11+
package(default_visibility = ["//visibility:public"])
12+
13+
ts_library(
14+
name = "benchmark",
15+
srcs = glob(
16+
include = ["src/**/*.ts"],
17+
exclude = [
18+
"src/**/*_spec.ts",
19+
"src/**/*_spec_large.ts",
20+
"src/**/*_benchmark.ts",
21+
],
22+
),
23+
module_name = "@angular-devkit/benchmark",
24+
module_root = "src/index.d.ts",
25+
deps = [
26+
"//packages/angular_devkit/core",
27+
"@rxjs",
28+
"@rxjs//operators",
29+
# @typings: node
30+
],
31+
)
32+
33+
ts_library(
34+
name = "benchmark_test_lib",
35+
srcs = glob(
36+
include = [
37+
"src/**/*_spec.ts",
38+
"src/**/*_spec_large.ts",
39+
],
40+
),
41+
data = [
42+
"src/test/exit-code-one.js",
43+
"src/test/fibonacci.js",
44+
"src/test/test-script.js",
45+
],
46+
deps = [
47+
":benchmark",
48+
"//packages/angular_devkit/core",
49+
"@rxjs",
50+
"@rxjs//operators",
51+
# @typings: jasmine
52+
# @typings: node
53+
],
54+
)
55+
56+
jasmine_node_test(
57+
name = "benchmark_test",
58+
srcs = [":benchmark_test_lib"],
59+
)
+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Angular Devkit Benchmark
2+
3+
This tool provides benchmark information for processes.
4+
The tool will gathering metrics from a given command, then average them out over the total runs.
5+
6+
It currently shows only time, process, cpu, and memory used but might be extended in the future.
7+
8+
This tool was created to provide an objective and reproducible way of benchmarking process
9+
performance.
10+
11+
Given a process (or its source code), process inputs and environment, keeping two of these elements
12+
constant while varying the third should allow for meaningful benchmarks over time.
13+
14+
In the context of the DevKit, we publish many CLI tools and have access to their source code.
15+
By tracking tool resource usage we can catch performance regressions or improvements on our CI.
16+
17+
18+
## STABILITY AND SUPPORT DISCLAIMER
19+
20+
This package is not currently stable. Usage, output and API may change at any time.
21+
Support is not ensured.
22+
23+
## Installation
24+
25+
You can install the benchmark tool via `npm install -g benchmark` for a global install, or without
26+
`-g` for a local install.
27+
Installing globally gives you access to the `benchmark` binary in your `PATH`.
28+
29+
30+
## CLI Usage
31+
32+
Call the `benchmark` binary, followed by options, then double dash, then the command to benchmark.
33+
34+
For more information on the available options, run `benchmark --help`:
35+
```
36+
$ benchmark --help
37+
[benchmark] benchmark [options] -- [command to benchmark]
38+
39+
Collects process stats from running the command.
40+
41+
Options:
42+
--help Show this message.
43+
(... other available options)
44+
45+
Example:
46+
benchmark --iterations=3 -- node my-script.js
47+
```
48+
49+
50+
## Example
51+
52+
Given the naive implementation of a fibonacci number calculator below:
53+
```
54+
// fibonacci.js
55+
const fib = (n) => n > 1 ? fib(n - 1) + fib(n - 2) : n;
56+
console.log(fib(parseInt(process.argv[2])));
57+
```
58+
59+
Run `benchmark -- node fibonacci.js 40` to benchmark calculating the 40th fibonacci number:
60+
61+
```
62+
$ benchmark -- node fibonacci.js 40
63+
[benchmark] Benchmarking process over 5 iterations, with up to 5 retries.
64+
[benchmark] node fibonacci.js 40 (at D:\sandbox\latest-project)
65+
[benchmark] Process Stats
66+
[benchmark] Elapsed Time: 2365.40 ms (2449.00, 2444.00, 2357.00, 2312.00, 2265.00)
67+
[benchmark] Average Process usage: 1.00 process(es) (1.00, 1.00, 1.00, 1.00, 1.00)
68+
[benchmark] Peak Process usage: 1.00 process(es) (1.00, 1.00, 1.00, 1.00, 1.00)
69+
[benchmark] Average CPU usage: 4.72 % (5.03, 4.86, 4.50, 4.50, 4.69)
70+
[benchmark] Peak CPU usage: 23.40 % (25.00, 23.40, 21.80, 21.80, 25.00)
71+
[benchmark] Average Memory usage: 22.34 MB (22.32, 22.34, 22.34, 22.35, 22.35)
72+
[benchmark] Peak Memory usage: 22.34 MB (22.32, 22.34, 22.34, 22.35, 22.35)
73+
```
74+
75+
76+
## API Usage
77+
78+
You can also use the benchmarking API directly:
79+
80+
```
81+
import { Command, defaultStatsCapture, runBenchmark } from '@angular-devkit/benchmark';
82+
83+
const command = new Command('node', ['fibonacci.js', '40']);
84+
const captures = [defaultStatsCapture];
85+
86+
runBenchmark({ command, command }).subscribe(results => {
87+
// results is:[{
88+
// "name": "Process Stats",
89+
// "metrics": [{
90+
// "name": "Elapsed Time", "unit": "ms", "value": 1883.6,
91+
// "componentValues": [1733, 1957, 1580, 1763, 2385]
92+
// }, {
93+
// "name": "Average Process usage", "unit": "process(es)", "value": 1,
94+
// "componentValues": [1, 1, 1, 1, 1]
95+
// }, {
96+
// "name": "Peak Process usage", "unit": "process(es)", "value": 1,
97+
// "componentValues": [1, 1, 1, 1, 1]
98+
// }, {
99+
// "name": "Average CPU usage", "unit": "%", "value": 3.0855555555555556,
100+
// "componentValues": [1.9625, 1.9500000000000002, 1.9500000000000002, 4.887499999999999, 4.677777777777778]
101+
// }, {
102+
// "name": "Peak CPU usage", "unit": "%", "value": 19.380000000000003,
103+
// "componentValues": [15.7, 15.6, 15.6, 25, 25]
104+
// }, {
105+
// "name": "Average Memory usage", "unit": "MB", "value": 22.364057600000002,
106+
// "componentValues": [22.383104, 22.332416, 22.401024, 22.355968, 22.347776]
107+
// }, {
108+
// "name": "Peak Memory usage", "unit": "MB", "value": 22.3649792,
109+
// "componentValues": [22.384639999999997, 22.335487999999998, 22.401024, 22.355968, 22.347776]
110+
// }]
111+
// }]
112+
});
113+
```
114+
115+
A good example of API usage is the `main` binary itself, found in `./src/main.ts`.
116+
We recommend using TypeScript to get full access to the interfaces included.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@angular-devkit/benchmark",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "Angular Benchmark",
6+
"bin": {
7+
"benchmark": "./src/main.js"
8+
},
9+
"keywords": [
10+
"benchmark"
11+
],
12+
"engines": {
13+
"node": ">= 8.9.0",
14+
"npm": ">= 5.5.1"
15+
},
16+
"dependencies": {
17+
"@angular-devkit/core": "0.0.0",
18+
"minimist": "^1.2.0",
19+
"pidusage": "^2.0.16",
20+
"pidtree": "^0.3.0",
21+
"rxjs": "~6.2.0",
22+
"tree-kill": "^1.2.0"
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export class Command {
10+
constructor(
11+
public cmd: string,
12+
public args: string[] = [],
13+
public cwd: string = process.cwd(),
14+
public expectedExitCode = 0,
15+
) { }
16+
17+
toString() {
18+
const { cmd, args, cwd } = this;
19+
const argsStr = args.length > 0 ? ' ' + args.join(' ') : '';
20+
21+
return `${cmd}${argsStr} (at ${cwd})`;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { logging, tags } from '@angular-devkit/core';
9+
import { AggregatedMetric, BenchmarkReporter, Metric } from './interfaces';
10+
11+
export const defaultReporter = (logger: logging.Logger): BenchmarkReporter => (process, groups) => {
12+
const toplevelLogger = logger;
13+
const indentLogger = new logging.IndentLogger('benchmark-indent-logger', toplevelLogger);
14+
15+
const formatMetric = (metric: Metric | AggregatedMetric) => tags.oneLine`
16+
${metric.name}: ${metric.value.toFixed(2)} ${metric.unit}
17+
${metric.componentValues ? `(${metric.componentValues.map(v => v.toFixed(2)).join(', ')})` : ''}
18+
`;
19+
20+
groups.forEach(group => {
21+
toplevelLogger.info(`${group.name}`);
22+
group.metrics.forEach(metric => indentLogger.info(formatMetric(metric)));
23+
});
24+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { Observable } from 'rxjs';
10+
import { map, reduce } from 'rxjs/operators';
11+
import { AggregatedProcessStats, Capture, MetricGroup, MonitoredProcess } from './interfaces';
12+
import { cumulativeMovingAverage, max } from './utils';
13+
14+
15+
export const defaultStatsCapture: Capture = (
16+
process: MonitoredProcess,
17+
): Observable<MetricGroup> => {
18+
type Accumulator = {
19+
elapsed: number,
20+
avgProcesses: number,
21+
peakProcesses: number,
22+
avgCpu: number,
23+
peakCpu: number,
24+
avgMemory: number,
25+
peakMemory: number,
26+
};
27+
const seed: Accumulator = {
28+
elapsed: 0,
29+
avgProcesses: 0,
30+
peakProcesses: 0,
31+
avgCpu: 0,
32+
peakCpu: 0,
33+
avgMemory: 0,
34+
peakMemory: 0,
35+
};
36+
37+
return process.stats$.pipe(
38+
reduce<AggregatedProcessStats, Accumulator>((acc, val, idx) => ({
39+
elapsed: val.elapsed,
40+
avgProcesses: cumulativeMovingAverage(acc.avgProcesses, val.processes, idx),
41+
peakProcesses: max(acc.peakProcesses, val.processes),
42+
avgCpu: cumulativeMovingAverage(acc.avgCpu, val.cpu, idx),
43+
peakCpu: max(acc.peakCpu, val.cpu),
44+
avgMemory: cumulativeMovingAverage(acc.avgMemory, val.memory, idx),
45+
peakMemory: max(acc.peakMemory, val.memory),
46+
}), seed),
47+
map(metrics => ({
48+
name: 'Process Stats',
49+
metrics: [
50+
{ name: 'Elapsed Time', unit: 'ms', value: metrics.elapsed },
51+
{ name: 'Average Process usage', unit: 'process(es)', value: metrics.avgProcesses },
52+
{ name: 'Peak Process usage', unit: 'process(es)', value: metrics.peakProcesses },
53+
{ name: 'Average CPU usage', unit: '%', value: metrics.avgCpu },
54+
{ name: 'Peak CPU usage', unit: '%', value: metrics.peakCpu },
55+
{ name: 'Average Memory usage', unit: 'MB', value: metrics.avgMemory * 1e-6 },
56+
{ name: 'Peak Memory usage', unit: 'MB', value: metrics.peakMemory * 1e-6 },
57+
],
58+
})),
59+
);
60+
};

0 commit comments

Comments
 (0)