overtake 1.4.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -15
- package/build/cli.js +126 -125
- package/build/executor.d.ts +6 -2
- package/build/executor.js +59 -46
- package/build/gc-watcher.js +2 -6
- package/build/index.d.ts +10 -11
- package/build/index.js +153 -155
- package/build/register-hook.d.ts +1 -0
- package/build/register-hook.js +15 -0
- package/build/reporter.d.ts +10 -2
- package/build/reporter.js +176 -214
- package/build/runner.d.ts +1 -1
- package/build/runner.js +128 -119
- package/build/types.d.ts +6 -6
- package/build/types.js +9 -14
- package/build/utils.d.ts +1 -17
- package/build/utils.js +53 -85
- package/build/worker.js +25 -24
- package/package.json +7 -25
- package/src/__tests__/assert-no-closure.ts +135 -0
- package/src/__tests__/benchmark-execute.ts +48 -0
- package/src/cli.ts +137 -142
- package/src/executor.ts +45 -15
- package/src/index.ts +85 -57
- package/src/register-hook.ts +15 -0
- package/src/reporter.ts +26 -18
- package/src/runner.ts +1 -4
- package/src/types.ts +8 -8
- package/src/utils.ts +15 -54
- package/src/worker.ts +5 -2
- package/tsconfig.json +2 -1
- package/build/cli.cjs +0 -179
- package/build/cli.cjs.map +0 -1
- package/build/cli.js.map +0 -1
- package/build/executor.cjs +0 -123
- package/build/executor.cjs.map +0 -1
- package/build/executor.js.map +0 -1
- package/build/gc-watcher.cjs +0 -30
- package/build/gc-watcher.cjs.map +0 -1
- package/build/gc-watcher.js.map +0 -1
- package/build/index.cjs +0 -442
- package/build/index.cjs.map +0 -1
- package/build/index.js.map +0 -1
- package/build/reporter.cjs +0 -311
- package/build/reporter.cjs.map +0 -1
- package/build/reporter.js.map +0 -1
- package/build/runner.cjs +0 -532
- package/build/runner.cjs.map +0 -1
- package/build/runner.js.map +0 -1
- package/build/types.cjs +0 -66
- package/build/types.cjs.map +0 -1
- package/build/types.js.map +0 -1
- package/build/utils.cjs +0 -174
- package/build/utils.cjs.map +0 -1
- package/build/utils.js.map +0 -1
- package/build/worker.cjs +0 -155
- package/build/worker.cjs.map +0 -1
- package/build/worker.js.map +0 -1
- package/src/__tests__/assert-no-closure.js +0 -134
package/build/index.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { cpus } from 'node:os';
|
|
2
2
|
import Progress from 'progress';
|
|
3
|
-
import { createExecutor } from
|
|
4
|
-
import { DEFAULT_CYCLES } from
|
|
3
|
+
import { createExecutor } from './executor.js';
|
|
4
|
+
import { DEFAULT_CYCLES } from './types.js';
|
|
5
5
|
export const DEFAULT_WORKERS = Math.max(1, Math.ceil(cpus().length / 4));
|
|
6
|
-
export const AsyncFunction = (async ()=>{}).constructor;
|
|
7
6
|
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
8
|
-
export const DEFAULT_REPORT_TYPES = [
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export const DEFAULT_REPORT_TYPES = ['ops'];
|
|
8
|
+
const createExecutorErrorReport = (error) => ({
|
|
9
|
+
count: 0,
|
|
10
|
+
heapUsedKB: 0,
|
|
11
|
+
dceWarning: false,
|
|
12
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13
|
+
});
|
|
11
14
|
export class MeasureContext {
|
|
12
|
-
title;
|
|
13
|
-
run;
|
|
14
15
|
pre;
|
|
15
16
|
post;
|
|
16
|
-
|
|
17
|
+
title;
|
|
18
|
+
run;
|
|
19
|
+
constructor(title, run) {
|
|
17
20
|
this.title = title;
|
|
18
21
|
this.run = run;
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
export class Measure {
|
|
22
25
|
#ctx;
|
|
23
|
-
constructor(ctx){
|
|
26
|
+
constructor(ctx) {
|
|
24
27
|
this.#ctx = ctx;
|
|
25
28
|
}
|
|
26
29
|
pre(fn) {
|
|
@@ -33,18 +36,18 @@ export class Measure {
|
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
export class TargetContext {
|
|
36
|
-
title;
|
|
37
|
-
setup;
|
|
38
39
|
teardown;
|
|
39
40
|
measures = [];
|
|
40
|
-
|
|
41
|
+
title;
|
|
42
|
+
setup;
|
|
43
|
+
constructor(title, setup) {
|
|
41
44
|
this.title = title;
|
|
42
45
|
this.setup = setup;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
export class Target {
|
|
46
49
|
#ctx;
|
|
47
|
-
constructor(ctx){
|
|
50
|
+
constructor(ctx) {
|
|
48
51
|
this.#ctx = ctx;
|
|
49
52
|
}
|
|
50
53
|
teardown(fn) {
|
|
@@ -60,7 +63,7 @@ export class Target {
|
|
|
60
63
|
export class FeedContext {
|
|
61
64
|
title;
|
|
62
65
|
fn;
|
|
63
|
-
constructor(title, fn){
|
|
66
|
+
constructor(title, fn) {
|
|
64
67
|
this.title = title;
|
|
65
68
|
this.fn = fn;
|
|
66
69
|
}
|
|
@@ -72,14 +75,16 @@ export class Benchmark {
|
|
|
72
75
|
static create(title, fn) {
|
|
73
76
|
if (fn) {
|
|
74
77
|
return new Benchmark(title, fn);
|
|
75
|
-
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
76
80
|
return new Benchmark(title);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
|
-
constructor(title, fn){
|
|
83
|
+
constructor(title, fn) {
|
|
80
84
|
if (fn) {
|
|
81
85
|
this.feed(title, fn);
|
|
82
|
-
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
83
88
|
this.feed(title);
|
|
84
89
|
}
|
|
85
90
|
}
|
|
@@ -93,14 +98,14 @@ export class Benchmark {
|
|
|
93
98
|
this.#targets.push(target);
|
|
94
99
|
return new Target(target);
|
|
95
100
|
}
|
|
96
|
-
async execute(options) {
|
|
97
|
-
const { workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, gcObserver = true, reportTypes = DEFAULT_REPORT_TYPES, progress = false, progressInterval = 100 } = options;
|
|
101
|
+
async execute(options = {}) {
|
|
102
|
+
const { workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, gcObserver = true, reportTypes = DEFAULT_REPORT_TYPES, progress = false, progressInterval = 100, } = options;
|
|
98
103
|
if (this.#executed) {
|
|
99
104
|
throw new Error("Benchmark is executed and can't be reused");
|
|
100
105
|
}
|
|
101
106
|
this.#executed = true;
|
|
102
107
|
const benchmarkUrl = options[BENCHMARK_URL];
|
|
103
|
-
const totalBenchmarks = this.#targets.reduce((acc, t)=>acc + t.measures.length * this.#feeds.length, 0);
|
|
108
|
+
const totalBenchmarks = this.#targets.reduce((acc, t) => acc + t.measures.length * this.#feeds.length, 0);
|
|
104
109
|
const progressMap = new Map();
|
|
105
110
|
let completedBenchmarks = 0;
|
|
106
111
|
let bar = null;
|
|
@@ -109,19 +114,17 @@ export class Benchmark {
|
|
|
109
114
|
total: totalBenchmarks * 100,
|
|
110
115
|
width: 30,
|
|
111
116
|
complete: '=',
|
|
112
|
-
incomplete: ' '
|
|
117
|
+
incomplete: ' ',
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
|
-
const onProgress = progress
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
...progressMap.values()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
124
|
-
} : undefined;
|
|
120
|
+
const onProgress = progress
|
|
121
|
+
? (info) => {
|
|
122
|
+
progressMap.set(info.id, info.progress);
|
|
123
|
+
const totalProgress = (completedBenchmarks + [...progressMap.values()].reduce((a, b) => a + b, 0)) * 100;
|
|
124
|
+
const label = info.id.length > 30 ? info.id.slice(0, 27) + '...' : info.id;
|
|
125
|
+
bar?.update(totalProgress / (totalBenchmarks * 100), { label });
|
|
126
|
+
}
|
|
127
|
+
: undefined;
|
|
125
128
|
const executor = createExecutor({
|
|
126
129
|
workers,
|
|
127
130
|
warmupCycles,
|
|
@@ -133,68 +136,78 @@ export class Benchmark {
|
|
|
133
136
|
reportTypes,
|
|
134
137
|
onProgress,
|
|
135
138
|
progressInterval,
|
|
136
|
-
[BENCHMARK_URL]: benchmarkUrl
|
|
139
|
+
[BENCHMARK_URL]: benchmarkUrl,
|
|
137
140
|
});
|
|
138
141
|
const reports = [];
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
for (const feed of this.#feeds){
|
|
150
|
-
const id = `${target.title}/${measure.title}/${feed.title}`;
|
|
151
|
-
const data = await feed.fn?.();
|
|
152
|
-
executor.push({
|
|
153
|
-
id,
|
|
154
|
-
setup: target.setup,
|
|
155
|
-
teardown: target.teardown,
|
|
156
|
-
pre: measure.pre,
|
|
157
|
-
run: measure.run,
|
|
158
|
-
post: measure.post,
|
|
159
|
-
data
|
|
160
|
-
}).then((data)=>{
|
|
161
|
-
progressMap.delete(id);
|
|
162
|
-
completedBenchmarks++;
|
|
163
|
-
measureReport.feeds.push({
|
|
142
|
+
const pendingReports = [];
|
|
143
|
+
try {
|
|
144
|
+
const feedData = await Promise.all(this.#feeds.map(async (feed) => ({ title: feed.title, data: await feed.fn?.() })));
|
|
145
|
+
for (const target of this.#targets) {
|
|
146
|
+
const targetReport = { target: target.title, measures: [] };
|
|
147
|
+
for (const measure of target.measures) {
|
|
148
|
+
const measureReport = { measure: measure.title, feeds: [] };
|
|
149
|
+
for (const feed of feedData) {
|
|
150
|
+
const id = `${target.title}/${measure.title}/${feed.title}`;
|
|
151
|
+
const feedReport = {
|
|
164
152
|
feed: feed.title,
|
|
165
|
-
data
|
|
166
|
-
}
|
|
167
|
-
|
|
153
|
+
data: createExecutorErrorReport('Benchmark did not produce a report'),
|
|
154
|
+
};
|
|
155
|
+
measureReport.feeds.push(feedReport);
|
|
156
|
+
pendingReports.push((async () => {
|
|
157
|
+
try {
|
|
158
|
+
feedReport.data = await executor.pushAsync({
|
|
159
|
+
id,
|
|
160
|
+
setup: target.setup,
|
|
161
|
+
teardown: target.teardown,
|
|
162
|
+
pre: measure.pre,
|
|
163
|
+
run: measure.run,
|
|
164
|
+
post: measure.post,
|
|
165
|
+
data: feed.data,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
feedReport.data = createExecutorErrorReport(error);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
progressMap.delete(id);
|
|
173
|
+
completedBenchmarks++;
|
|
174
|
+
}
|
|
175
|
+
})());
|
|
176
|
+
}
|
|
177
|
+
targetReport.measures.push(measureReport);
|
|
168
178
|
}
|
|
169
|
-
|
|
179
|
+
reports.push(targetReport);
|
|
170
180
|
}
|
|
171
|
-
|
|
181
|
+
await Promise.all(pendingReports);
|
|
182
|
+
if (bar) {
|
|
183
|
+
bar.update(1, { label: 'done' });
|
|
184
|
+
bar.terminate();
|
|
185
|
+
}
|
|
186
|
+
return reports;
|
|
172
187
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (bar) {
|
|
176
|
-
bar.update(1, {
|
|
177
|
-
label: 'done'
|
|
178
|
-
});
|
|
179
|
-
bar.terminate();
|
|
188
|
+
finally {
|
|
189
|
+
executor.kill();
|
|
180
190
|
}
|
|
181
|
-
return reports;
|
|
182
191
|
}
|
|
183
192
|
}
|
|
184
|
-
export const printSimpleReports = (reports)=>{
|
|
185
|
-
for (const report of reports){
|
|
186
|
-
for (const { measure, feeds } of report.measures){
|
|
193
|
+
export const printSimpleReports = (reports) => {
|
|
194
|
+
for (const report of reports) {
|
|
195
|
+
for (const { measure, feeds } of report.measures) {
|
|
187
196
|
console.group('\n', report.target, measure);
|
|
188
|
-
for (const { feed, data } of feeds){
|
|
197
|
+
for (const { feed, data } of feeds) {
|
|
189
198
|
const { count, heapUsedKB, dceWarning, error: benchError, ...metrics } = data;
|
|
190
199
|
if (benchError) {
|
|
191
200
|
console.log(feed, `\x1b[31m[error: ${benchError}]\x1b[0m`);
|
|
192
201
|
continue;
|
|
193
202
|
}
|
|
194
|
-
const output = Object.entries(metrics)
|
|
203
|
+
const output = Object.entries(metrics)
|
|
204
|
+
.map(([key, report]) => `${key}: ${report.toString()}`)
|
|
205
|
+
.join('; ');
|
|
195
206
|
const extras = [];
|
|
196
|
-
if (heapUsedKB)
|
|
197
|
-
|
|
207
|
+
if (heapUsedKB)
|
|
208
|
+
extras.push(`heap: ${heapUsedKB}KB`);
|
|
209
|
+
if (dceWarning)
|
|
210
|
+
extras.push('\x1b[33m[DCE warning]\x1b[0m');
|
|
198
211
|
const extrasStr = extras.length > 0 ? ` (${extras.join(', ')})` : '';
|
|
199
212
|
console.log(feed, output + extrasStr);
|
|
200
213
|
}
|
|
@@ -202,44 +215,36 @@ export const printSimpleReports = (reports)=>{
|
|
|
202
215
|
}
|
|
203
216
|
}
|
|
204
217
|
};
|
|
205
|
-
export const printTableReports = (reports)=>{
|
|
206
|
-
for (const report of reports){
|
|
207
|
-
for (const { measure, feeds } of report.measures){
|
|
218
|
+
export const printTableReports = (reports) => {
|
|
219
|
+
for (const report of reports) {
|
|
220
|
+
for (const { measure, feeds } of report.measures) {
|
|
208
221
|
console.log('\n', report.target, measure);
|
|
209
222
|
const table = {};
|
|
210
|
-
for (const { feed, data } of feeds){
|
|
223
|
+
for (const { feed, data } of feeds) {
|
|
211
224
|
const { error: benchError } = data;
|
|
212
225
|
if (benchError) {
|
|
213
|
-
table[feed] = {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
table[feed] = Object.fromEntries(Object.entries(data).map(([key, report])=>[
|
|
218
|
-
key,
|
|
219
|
-
report.toString()
|
|
220
|
-
]));
|
|
226
|
+
table[feed] = { error: benchError };
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
232
|
console.table(table);
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
};
|
|
227
|
-
export const printJSONReports = (reports, padding)=>{
|
|
236
|
+
export const printJSONReports = (reports, padding) => {
|
|
228
237
|
const output = {};
|
|
229
|
-
for (const report of reports){
|
|
230
|
-
for (const { measure, feeds } of report.measures){
|
|
238
|
+
for (const report of reports) {
|
|
239
|
+
for (const { measure, feeds } of report.measures) {
|
|
231
240
|
const row = {};
|
|
232
|
-
for (const { feed, data } of feeds){
|
|
241
|
+
for (const { feed, data } of feeds) {
|
|
233
242
|
const { error: benchError } = data;
|
|
234
243
|
if (benchError) {
|
|
235
|
-
row[feed] = {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
row[feed] = Object.fromEntries(Object.entries(data).map(([key, report])=>[
|
|
240
|
-
key,
|
|
241
|
-
report.toString()
|
|
242
|
-
]));
|
|
244
|
+
row[feed] = { error: String(benchError) };
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
output[`${report.target} ${measure}`] = row;
|
|
@@ -247,83 +252,74 @@ export const printJSONReports = (reports, padding)=>{
|
|
|
247
252
|
}
|
|
248
253
|
console.log(JSON.stringify(output, null, padding));
|
|
249
254
|
};
|
|
250
|
-
export const printMarkdownReports = (reports)=>{
|
|
251
|
-
for (const report of reports){
|
|
252
|
-
for (const { measure, feeds } of report.measures){
|
|
255
|
+
export const printMarkdownReports = (reports) => {
|
|
256
|
+
for (const report of reports) {
|
|
257
|
+
for (const { measure, feeds } of report.measures) {
|
|
253
258
|
console.log(`\n## ${report.target} - ${measure}\n`);
|
|
254
|
-
if (feeds.length === 0)
|
|
255
|
-
|
|
259
|
+
if (feeds.length === 0)
|
|
260
|
+
continue;
|
|
261
|
+
const firstValid = feeds.find((f) => !f.data.error);
|
|
256
262
|
if (!firstValid) {
|
|
257
|
-
for (const { feed, data } of feeds){
|
|
263
|
+
for (const { feed, data } of feeds) {
|
|
258
264
|
console.log(`| ${feed} | error: ${data.error} |`);
|
|
259
265
|
}
|
|
260
266
|
continue;
|
|
261
267
|
}
|
|
262
|
-
const keys = Object.keys(firstValid.data).filter((k)=>k !== 'count' && k !== 'error');
|
|
263
|
-
const header = [
|
|
264
|
-
|
|
265
|
-
...keys
|
|
266
|
-
].join(' | ');
|
|
267
|
-
const separator = [
|
|
268
|
-
'---',
|
|
269
|
-
...keys.map(()=>'---')
|
|
270
|
-
].join(' | ');
|
|
268
|
+
const keys = Object.keys(firstValid.data).filter((k) => k !== 'count' && k !== 'error');
|
|
269
|
+
const header = ['Feed', ...keys].join(' | ');
|
|
270
|
+
const separator = ['---', ...keys.map(() => '---')].join(' | ');
|
|
271
271
|
console.log(`| ${header} |`);
|
|
272
272
|
console.log(`| ${separator} |`);
|
|
273
|
-
for (const { feed, data } of feeds){
|
|
273
|
+
for (const { feed, data } of feeds) {
|
|
274
274
|
if (data.error) {
|
|
275
275
|
console.log(`| ${feed} | error: ${data.error} |`);
|
|
276
276
|
continue;
|
|
277
277
|
}
|
|
278
|
-
const values = keys.map((k)=>data[k]?.toString() ?? '-');
|
|
279
|
-
console.log(`| ${[
|
|
280
|
-
feed,
|
|
281
|
-
...values
|
|
282
|
-
].join(' | ')} |`);
|
|
278
|
+
const values = keys.map((k) => data[k]?.toString() ?? '-');
|
|
279
|
+
console.log(`| ${[feed, ...values].join(' | ')} |`);
|
|
283
280
|
}
|
|
284
281
|
}
|
|
285
282
|
}
|
|
286
283
|
};
|
|
287
|
-
export const printHistogramReports = (reports, width = 40)=>{
|
|
288
|
-
for (const report of reports){
|
|
289
|
-
for (const { measure, feeds } of report.measures){
|
|
284
|
+
export const printHistogramReports = (reports, width = 40) => {
|
|
285
|
+
for (const report of reports) {
|
|
286
|
+
for (const { measure, feeds } of report.measures) {
|
|
290
287
|
console.log(`\n${report.target} - ${measure}\n`);
|
|
291
288
|
const opsKey = 'ops';
|
|
292
|
-
const values = feeds.map((f)=>{
|
|
289
|
+
const values = feeds.map((f) => {
|
|
293
290
|
const { error: benchError } = f.data;
|
|
294
291
|
return {
|
|
295
292
|
feed: f.feed,
|
|
296
|
-
value: benchError ? 0 : f.data[opsKey]?.valueOf() ?? 0,
|
|
297
|
-
error: benchError
|
|
293
|
+
value: benchError ? 0 : (f.data[opsKey]?.valueOf() ?? 0),
|
|
294
|
+
error: benchError,
|
|
298
295
|
};
|
|
299
296
|
});
|
|
300
|
-
const maxValue = Math.max(...values.map((v)=>v.value));
|
|
301
|
-
const maxLabelLen = Math.max(...values.map((v)=>v.feed.length));
|
|
302
|
-
for (const { feed, value, error } of values){
|
|
297
|
+
const maxValue = Math.max(...values.map((v) => v.value));
|
|
298
|
+
const maxLabelLen = Math.max(...values.map((v) => v.feed.length));
|
|
299
|
+
for (const { feed, value, error } of values) {
|
|
303
300
|
const label = feed.padEnd(maxLabelLen);
|
|
304
301
|
if (error) {
|
|
305
302
|
console.log(` ${label} | \x1b[31m[error: ${error}]\x1b[0m`);
|
|
306
303
|
continue;
|
|
307
304
|
}
|
|
308
|
-
const barLen = maxValue > 0 ? Math.round(value / maxValue * width) : 0;
|
|
305
|
+
const barLen = maxValue > 0 ? Math.round((value / maxValue) * width) : 0;
|
|
309
306
|
const bar = '\u2588'.repeat(barLen);
|
|
310
|
-
const formatted = value.toLocaleString('en-US', {
|
|
311
|
-
maximumFractionDigits: 2
|
|
312
|
-
});
|
|
307
|
+
const formatted = value.toLocaleString('en-US', { maximumFractionDigits: 2 });
|
|
313
308
|
console.log(` ${label} | ${bar} ${formatted} ops/s`);
|
|
314
309
|
}
|
|
315
310
|
}
|
|
316
311
|
}
|
|
317
312
|
};
|
|
318
|
-
export const reportsToBaseline = (reports)=>{
|
|
313
|
+
export const reportsToBaseline = (reports) => {
|
|
319
314
|
const results = {};
|
|
320
|
-
for (const report of reports){
|
|
321
|
-
for (const { measure, feeds } of report.measures){
|
|
322
|
-
for (const { feed, data } of feeds){
|
|
323
|
-
if (data.error)
|
|
315
|
+
for (const report of reports) {
|
|
316
|
+
for (const { measure, feeds } of report.measures) {
|
|
317
|
+
for (const { feed, data } of feeds) {
|
|
318
|
+
if (data.error)
|
|
319
|
+
continue;
|
|
324
320
|
const key = `${report.target}/${measure}/${feed}`;
|
|
325
321
|
results[key] = {};
|
|
326
|
-
for (const [metric, value] of Object.entries(data)){
|
|
322
|
+
for (const [metric, value] of Object.entries(data)) {
|
|
327
323
|
if (metric !== 'count' && typeof value.valueOf === 'function') {
|
|
328
324
|
results[key][metric] = value.valueOf();
|
|
329
325
|
}
|
|
@@ -334,14 +330,14 @@ export const reportsToBaseline = (reports)=>{
|
|
|
334
330
|
return {
|
|
335
331
|
version: 1,
|
|
336
332
|
timestamp: new Date().toISOString(),
|
|
337
|
-
results
|
|
333
|
+
results,
|
|
338
334
|
};
|
|
339
335
|
};
|
|
340
|
-
export const printComparisonReports = (reports, baseline, threshold = 5)=>{
|
|
341
|
-
for (const report of reports){
|
|
342
|
-
for (const { measure, feeds } of report.measures){
|
|
336
|
+
export const printComparisonReports = (reports, baseline, threshold = 5) => {
|
|
337
|
+
for (const report of reports) {
|
|
338
|
+
for (const { measure, feeds } of report.measures) {
|
|
343
339
|
console.log(`\n${report.target} - ${measure}\n`);
|
|
344
|
-
for (const { feed, data } of feeds){
|
|
340
|
+
for (const { feed, data } of feeds) {
|
|
345
341
|
const key = `${report.target}/${measure}/${feed}`;
|
|
346
342
|
const baselineData = baseline.results[key];
|
|
347
343
|
console.log(` ${feed}:`);
|
|
@@ -349,22 +345,26 @@ export const printComparisonReports = (reports, baseline, threshold = 5)=>{
|
|
|
349
345
|
console.log(` \x1b[31m[error: ${data.error}]\x1b[0m`);
|
|
350
346
|
continue;
|
|
351
347
|
}
|
|
352
|
-
for (const [metric, value] of Object.entries(data)){
|
|
353
|
-
if (metric === 'count')
|
|
348
|
+
for (const [metric, value] of Object.entries(data)) {
|
|
349
|
+
if (metric === 'count')
|
|
350
|
+
continue;
|
|
354
351
|
const current = value.valueOf();
|
|
355
352
|
const baselineValue = baselineData?.[metric];
|
|
356
353
|
if (baselineValue !== undefined && baselineValue !== 0) {
|
|
357
|
-
const change = (current - baselineValue) / baselineValue * 100;
|
|
354
|
+
const change = ((current - baselineValue) / baselineValue) * 100;
|
|
358
355
|
const isOps = metric === 'ops';
|
|
359
356
|
const improved = isOps ? change > threshold : change < -threshold;
|
|
360
357
|
const regressed = isOps ? change < -threshold : change > threshold;
|
|
361
358
|
let indicator = ' ';
|
|
362
|
-
if (improved)
|
|
363
|
-
|
|
359
|
+
if (improved)
|
|
360
|
+
indicator = '\x1b[32m+\x1b[0m';
|
|
361
|
+
else if (regressed)
|
|
362
|
+
indicator = '\x1b[31m!\x1b[0m';
|
|
364
363
|
const changeStr = change >= 0 ? `+${change.toFixed(1)}%` : `${change.toFixed(1)}%`;
|
|
365
364
|
const coloredChange = regressed ? `\x1b[31m${changeStr}\x1b[0m` : improved ? `\x1b[32m${changeStr}\x1b[0m` : changeStr;
|
|
366
365
|
console.log(` ${indicator} ${metric}: ${value.toString()} (${coloredChange})`);
|
|
367
|
-
}
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
368
|
console.log(` * ${metric}: ${value.toString()} (new)`);
|
|
369
369
|
}
|
|
370
370
|
}
|
|
@@ -373,5 +373,3 @@ export const printComparisonReports = (reports, baseline, threshold = 5)=>{
|
|
|
373
373
|
}
|
|
374
374
|
console.log(`\nBaseline from: ${baseline.timestamp}`);
|
|
375
375
|
};
|
|
376
|
-
|
|
377
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { register } from 'node:module';
|
|
2
|
+
async function resolve(s, c, n) {
|
|
3
|
+
try {
|
|
4
|
+
return await n(s, c);
|
|
5
|
+
}
|
|
6
|
+
catch (e) {
|
|
7
|
+
if (s.endsWith('.js'))
|
|
8
|
+
try {
|
|
9
|
+
return await n(s.slice(0, -3) + '.ts', c);
|
|
10
|
+
}
|
|
11
|
+
catch { }
|
|
12
|
+
throw e;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
register('data:text/javascript,' + encodeURIComponent(`export ${resolve.toString()}`));
|
package/build/reporter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReportType } from './types.js';
|
|
1
|
+
import { type ReportType } from './types.js';
|
|
2
2
|
export declare class Report {
|
|
3
3
|
readonly type: ReportType;
|
|
4
4
|
readonly value: bigint;
|
|
@@ -8,4 +8,12 @@ export declare class Report {
|
|
|
8
8
|
valueOf(): number;
|
|
9
9
|
toString(): string;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
type Stats = {
|
|
12
|
+
sum: bigint;
|
|
13
|
+
mean: bigint;
|
|
14
|
+
ssd: bigint;
|
|
15
|
+
n: bigint;
|
|
16
|
+
};
|
|
17
|
+
export declare const computeStats: (durations: BigUint64Array) => Stats;
|
|
18
|
+
export declare const createReport: (durations: BigUint64Array, type: ReportType, stats?: Stats) => Report;
|
|
19
|
+
export {};
|