1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-23 01:27:14 +02:00
pigallery2/benchmark/Benchmark.ts

196 lines
5.1 KiB
TypeScript
Raw Normal View History

2020-12-30 22:13:19 +02:00
import {BenchmarkResult} from './BenchmarkRunner';
import {ContentWrapper} from '../src/common/entities/ConentWrapper';
2020-12-31 13:35:28 +02:00
import {Express} from 'express';
import {Utils} from '../src/common/Utils';
import {Message} from '../src/common/entities/Message';
2020-12-30 22:13:19 +02:00
export interface BenchmarkStep {
name: string;
fn: ((input?: any) => Promise<ContentWrapper | any[] | void>);
}
2020-12-31 13:35:28 +02: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-30 22:13:19 +02:00
export class Benchmark {
steps: BenchmarkStep[] = [];
name: string;
2020-12-31 13:35:28 +02:00
request: any;
2020-12-30 22:13:19 +02:00
beforeEach: () => Promise<any>;
afterEach: () => Promise<any>;
2020-12-31 13:35:28 +02:00
private readonly bmExpressApp: BMExpressApp;
2020-12-30 22:13:19 +02:00
constructor(name: string,
2020-12-31 13:35:28 +02:00
request: any = {},
2020-12-30 22:13:19 +02:00
beforeEach?: () => Promise<any>,
afterEach?: () => Promise<any>) {
this.name = name;
2020-12-31 13:35:28 +02:00
this.request = request;
2020-12-30 22:13:19 +02:00
this.beforeEach = beforeEach;
this.afterEach = afterEach;
2020-12-31 13:35:28 +02:00
this.bmExpressApp = new BMExpressApp(this);
}
get BmExpressApp(): Express {
return (<unknown>this.bmExpressApp) as Express;
2020-12-30 22:13:19 +02: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 13:35:28 +02:00
outputToBMResult(name: string, output: any[] | ContentWrapper | Message<ContentWrapper>): BenchmarkResult {
2020-12-30 22:13:19 +02:00
if (output) {
if (Array.isArray(output)) {
return {
name: name,
duration: null,
items: output.length,
};
}
2020-12-31 13:35:28 +02:00
if (output instanceof ContentWrapper) {
2020-12-30 22:13:19 +02:00
return {
name: name,
duration: null,
contentWrapper: output
};
}
2020-12-31 13:35:28 +02: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-30 22:13:19 +02:00
}
return {
name: name,
duration: null
};
}
async scanSteps(): Promise<any[]> {
2020-12-31 13:35:28 +02:00
const request = Utils.clone(this.request);
2020-12-30 22:13:19 +02: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 13:35:28 +02:00
stepOutput[j] = await this.steps[i].fn(request);
2020-12-30 22:13:19 +02:00
}
if (this.afterEach) {
await this.afterEach();
}
}
return stepOutput;
}
async runOneRound(stepTimer: number[]): Promise<number[]> {
2020-12-31 13:35:28 +02:00
const request = Utils.clone(this.request);
2020-12-30 22:13:19 +02:00
for (let i = 0; i < this.steps.length; ++i) {
const start = process.hrtime();
2020-12-31 13:35:28 +02:00
await this.steps[i].fn(request);
2020-12-30 22:13:19 +02:00
const end = process.hrtime(start);
stepTimer[i] += (end[0] * 1000 + end[1] / 1000000);
}
return stepTimer;
}
addAStep(step: BenchmarkStep) {
this.steps.push(step);
}
}