2020-12-31 04:13:19 +08:00
|
|
|
import {BenchmarkResult} from './BenchmarkRunner';
|
|
|
|
import {ContentWrapper} from '../src/common/entities/ConentWrapper';
|
2020-12-31 19:35:28 +08:00
|
|
|
import {Express} from 'express';
|
|
|
|
import {Utils} from '../src/common/Utils';
|
|
|
|
import {Message} from '../src/common/entities/Message';
|
2020-12-31 04:13:19 +08:00
|
|
|
|
|
|
|
export interface BenchmarkStep {
|
|
|
|
name: string;
|
|
|
|
fn: ((input?: any) => Promise<ContentWrapper | any[] | void>);
|
|
|
|
}
|
|
|
|
|
2020-12-31 19:35:28 +08:00
|
|
|
/**
|
|
|
|
* This class converts PiGallery2 Routers to benchamrkable steps to the Benchmark class
|
|
|
|
*/
|
|
|
|
class BMExpressApp {
|
|
|
|
readonly benchmark: Benchmark;
|
|
|
|
|
|
|
|
|
|
|
|
constructor(benchmark: Benchmark) {
|
|
|
|
this.benchmark = benchmark;
|
|
|
|
}
|
|
|
|
|
|
|
|
get(match: string | string[], ...functions: ((req: any, res: any, next: Function) => void)[]) {
|
|
|
|
functions.forEach(f => {
|
|
|
|
this.benchmark.addAStep({
|
|
|
|
name: this.camelToSpaceSeparated(f.name),
|
|
|
|
fn: (request: any) => this.nextToPromise(f, request)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private camelToSpaceSeparated(text: string) {
|
|
|
|
const result = (text.replace(/([A-Z])/g, ' $1')).toLocaleLowerCase();
|
|
|
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
private nextToPromise(fn: (req: any, res: any, next: Function) => void, request: any) {
|
|
|
|
return new Promise<void>((resolve, reject) => {
|
|
|
|
const response = {
|
|
|
|
header: () => {
|
|
|
|
},
|
|
|
|
json: (data: any) => {
|
|
|
|
resolve(data);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
fn(request, response, (err?: any) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
resolve(request.resultPipe);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-31 04:13:19 +08:00
|
|
|
export class Benchmark {
|
|
|
|
|
|
|
|
|
|
|
|
steps: BenchmarkStep[] = [];
|
|
|
|
name: string;
|
2020-12-31 19:35:28 +08:00
|
|
|
request: any;
|
2020-12-31 04:13:19 +08:00
|
|
|
beforeEach: () => Promise<any>;
|
|
|
|
afterEach: () => Promise<any>;
|
2020-12-31 19:35:28 +08:00
|
|
|
private readonly bmExpressApp: BMExpressApp;
|
2020-12-31 04:13:19 +08:00
|
|
|
|
|
|
|
|
|
|
|
constructor(name: string,
|
2020-12-31 19:35:28 +08:00
|
|
|
request: any = {},
|
2020-12-31 04:13:19 +08:00
|
|
|
beforeEach?: () => Promise<any>,
|
|
|
|
afterEach?: () => Promise<any>) {
|
|
|
|
this.name = name;
|
2020-12-31 19:35:28 +08:00
|
|
|
this.request = request;
|
2020-12-31 04:13:19 +08:00
|
|
|
this.beforeEach = beforeEach;
|
|
|
|
this.afterEach = afterEach;
|
2020-12-31 19:35:28 +08:00
|
|
|
this.bmExpressApp = new BMExpressApp(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
get BmExpressApp(): Express {
|
|
|
|
return (<unknown>this.bmExpressApp) as Express;
|
2020-12-31 04:13:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async run(RUNS: number): Promise<BenchmarkResult> {
|
|
|
|
console.log('Running benchmark: ' + this.name);
|
|
|
|
const scanned = await this.scanSteps();
|
|
|
|
const start = process.hrtime();
|
|
|
|
let skip = 0;
|
|
|
|
const stepTimer = new Array(this.steps.length).fill(0);
|
|
|
|
for (let i = 0; i < RUNS; i++) {
|
|
|
|
if (this.beforeEach) {
|
|
|
|
const startSkip = process.hrtime();
|
|
|
|
await this.beforeEach();
|
|
|
|
const endSkip = process.hrtime(startSkip);
|
|
|
|
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
|
|
|
}
|
|
|
|
await this.runOneRound(stepTimer);
|
|
|
|
if (this.afterEach) {
|
|
|
|
const startSkip = process.hrtime();
|
|
|
|
await this.afterEach();
|
|
|
|
const endSkip = process.hrtime(startSkip);
|
|
|
|
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const end = process.hrtime(start);
|
|
|
|
const duration = (end[0] * 1000 + end[1] / 1000000 - skip) / RUNS;
|
|
|
|
|
|
|
|
|
|
|
|
const ret = this.outputToBMResult(this.name, scanned[scanned.length - 1]);
|
|
|
|
ret.duration = duration;
|
|
|
|
ret.subBenchmarks = scanned.map((o, i) => {
|
|
|
|
const stepBm = this.outputToBMResult(this.steps[i].name, o);
|
|
|
|
stepBm.duration = stepTimer[i] / RUNS;
|
|
|
|
return stepBm;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-31 19:35:28 +08:00
|
|
|
outputToBMResult(name: string, output: any[] | ContentWrapper | Message<ContentWrapper>): BenchmarkResult {
|
2020-12-31 04:13:19 +08:00
|
|
|
if (output) {
|
|
|
|
if (Array.isArray(output)) {
|
|
|
|
return {
|
|
|
|
name: name,
|
|
|
|
duration: null,
|
|
|
|
items: output.length,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-31 19:35:28 +08:00
|
|
|
if (output instanceof ContentWrapper) {
|
2020-12-31 04:13:19 +08:00
|
|
|
return {
|
|
|
|
name: name,
|
|
|
|
duration: null,
|
|
|
|
contentWrapper: output
|
|
|
|
};
|
|
|
|
}
|
2020-12-31 19:35:28 +08:00
|
|
|
if (output instanceof Message) {
|
|
|
|
const msg = output.result;
|
|
|
|
if (Array.isArray(msg)) {
|
|
|
|
return {
|
|
|
|
name: name,
|
|
|
|
duration: null,
|
|
|
|
items: msg.length,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg instanceof ContentWrapper) {
|
|
|
|
return {
|
|
|
|
name: name,
|
|
|
|
duration: null,
|
|
|
|
contentWrapper: msg
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-12-31 04:13:19 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
name: name,
|
|
|
|
duration: null
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async scanSteps(): Promise<any[]> {
|
2020-12-31 19:35:28 +08:00
|
|
|
const request = Utils.clone(this.request);
|
2020-12-31 04:13:19 +08:00
|
|
|
const stepOutput = new Array(this.steps.length);
|
|
|
|
|
|
|
|
for (let j = 0; j < this.steps.length; ++j) {
|
|
|
|
if (this.beforeEach) {
|
|
|
|
await this.beforeEach();
|
|
|
|
}
|
|
|
|
for (let i = 0; i <= j; ++i) {
|
2020-12-31 19:35:28 +08:00
|
|
|
stepOutput[j] = await this.steps[i].fn(request);
|
2020-12-31 04:13:19 +08:00
|
|
|
}
|
|
|
|
if (this.afterEach) {
|
|
|
|
await this.afterEach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stepOutput;
|
|
|
|
}
|
|
|
|
|
|
|
|
async runOneRound(stepTimer: number[]): Promise<number[]> {
|
2020-12-31 19:35:28 +08:00
|
|
|
const request = Utils.clone(this.request);
|
2020-12-31 04:13:19 +08:00
|
|
|
for (let i = 0; i < this.steps.length; ++i) {
|
|
|
|
const start = process.hrtime();
|
2020-12-31 19:35:28 +08:00
|
|
|
await this.steps[i].fn(request);
|
2020-12-31 04:13:19 +08:00
|
|
|
const end = process.hrtime(start);
|
|
|
|
stepTimer[i] += (end[0] * 1000 + end[1] / 1000000);
|
|
|
|
}
|
|
|
|
return stepTimer;
|
|
|
|
}
|
|
|
|
|
|
|
|
addAStep(step: BenchmarkStep) {
|
|
|
|
this.steps.push(step);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|