Skip to content

Commit

Permalink
fix: add internal async function checker, adjust divide number in hz …
Browse files Browse the repository at this point in the history
…metric compute
  • Loading branch information
kainstar authored and Aslemammad committed Oct 18, 2022
1 parent 9ceac22 commit bd15cf8
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 26 deletions.
22 changes: 11 additions & 11 deletions examples/src/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ await bench.run();
console.table(
bench.tasks.map(({ name, result }) => (result ? ({
'Task Name': name,
'ops/sec': result.hz,
'Average Time (ps)': result.mean * 1000,
'Variance (ps)': result.variance * 1000,
'ops/sec': parseInt(result.hz, 10).toLocaleString(),
'Average Time (ns)': result.mean * 1000 * 1000,
Margin: `\xb1${result.rme.toFixed(2)}%`,
Samples: result.samples.length,
}) : null)),
);

// Output:
// ┌─────────┬──────────────────┬────────────────────┬─────────────────────┬───────────────────────┬─────────┐
// │ (index) │ Task Name │ ops/sec Average Time (ps) │ Variance (ps) │ Samples │
// ├─────────┼──────────────────┼────────────────────┼─────────────────────┼───────────────────────┼─────────┤
// │ 0 │ 'switch 1' │ 14463.3718781234840.06914017066190119 │ 0.006014460930767669 │ 414836
// │ 1 │ 'switch 2' │ 14880.8677553375440.06720038215790977 │ 0.0004529273320957288458644
// │ 2 │ 'async switch 1' │ 7970.65255150332 │ 0.1254602422497256 │ 0.010617119343817942 │ 367049
// │ 3 │ 'async switch 2' │ 8702.8499135138680.11490488862127687 │ 0.002613875857497825 │ 389356
// └─────────┴──────────────────┴────────────────────┴─────────────────────┴───────────────────────┴─────────┘
// ┌─────────┬──────────────────┬──────────────┬──────────────────────────────┬─────────┐
// │ (index) │ Task Name │ ops/sec Average Time (ns) │ Margin │ Samples │
// ├─────────┼──────────────────┼──────────────┼──────────────────────────────┼─────────┤
// │ 0 │ 'switch 1' │ '15,370,548'65.05948714494949 │ '±1.89%' │ 1537055
// │ 1 │ 'switch 2' │ '14,826,005'67.44905232421046 │ '±2.69%'1482601
// │ 2 │ 'async switch 1' │ '8,196,259' │ 122.00687003709369 │ '±5.01%' │ 819626
// │ 3 │ 'async switch 2' │ '7,830,281'127.7093250277111 │ '±5.79%' │ 788521
// └─────────┴──────────────────┴──────────────┴──────────────────────────────┴─────────┘
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
"version": "2.3.0",
"type": "module",
"packageManager": "pnpm@7.5.1",
"engines": {
"node": ">= 10.0.0"
},
"scripts": {
"dev": "tsup --watch",
"build": "tsup",
Expand Down
5 changes: 4 additions & 1 deletion src/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
} from 'types/index';
import { createBenchEvent } from './event';
import Task from './task';
import { now } from './utils';
import { isAsyncFunction, isAsyncFunctionDirty, now } from './utils';

/**
* The Benchmark instance for keeping track of the benchmark tasks and controlling
Expand All @@ -32,6 +32,8 @@ export default class Bench extends EventTarget {

now = now;

isAsyncFunction: (fn: Fn) => boolean;

setup: Hook;

teardown: Hook;
Expand All @@ -42,6 +44,7 @@ export default class Bench extends EventTarget {
this.warmupTime = options.warmupTime ?? this.warmupTime;
this.warmupIterations = options.warmupIterations ?? this.warmupIterations;
this.time = options.time ?? this.time;
this.isAsyncFunction = options.dirtyAsyncCheck ? isAsyncFunctionDirty : isAsyncFunction;
this.iterations = options.iterations ?? this.iterations;
this.signal = options.signal;
// eslint-disable-next-line @typescript-eslint/no-empty-function
Expand Down
13 changes: 5 additions & 8 deletions src/task.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type {
Fn, TaskEvents, TaskResult, TaskEventsMap,
} from 'types/index';
import { types } from 'util';
import Bench from './bench';
import tTable from './constants';
import { createBenchEvent } from './event';
import { getMean, getSum, getVariance } from './utils';
import { getMean, getVariance } from './utils';

/**
* A class that represents each benchmark task in Tinybench. It keeps track of the
Expand Down Expand Up @@ -46,17 +45,16 @@ export default class Task extends EventTarget {
*/
async run() {
this.dispatchEvent(createBenchEvent('start', this));
const startTime = this.bench.now(); // ms
let totalTime = 0; // ms
const samples: number[] = [];
const isAsync = this.bench.isAsyncFunction(this.fn);

await this.bench.setup(this, 'run');
while (
(totalTime < this.bench.time || this.runs < this.bench.iterations)
&& !this.bench.signal?.aborted
) {
let taskStart = 0;
const isAsync = types.isAsyncFunction(this.fn);

try {
taskStart = this.bench.now();
Expand All @@ -68,11 +66,10 @@ export default class Task extends EventTarget {
const taskTime = this.bench.now() - taskStart;
this.runs += 1;
samples.push(taskTime);
totalTime += taskTime;
} catch (e) {
this.setResult({ error: e });
}

totalTime = this.bench.now() - startTime;
}
await this.bench.teardown(this, 'run');

Expand All @@ -81,8 +78,8 @@ export default class Task extends EventTarget {
{
const min = samples[0]!;
const max = samples[samples.length - 1]!;
const period = getSum(samples) / this.runs;
const hz = 1 / period;
const period = totalTime / this.runs;
const hz = 1000 / period;

// benchmark.js: https://github.com/bestiejs/benchmark.js/blob/42f3b732bac3640eddb3ae5f50e445f3141016fd/benchmark.js#L1912-L1927
const mean = getMean(samples);
Expand Down
22 changes: 20 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Fn } from 'types';

export const nanoToMs = (nano: number) => nano / 1e6;

export const now = () => {
Expand All @@ -20,7 +22,23 @@ export const getVariance = (samples: number[], mean: number) => {
return result / (samples.length - 1) || 0;
};

// eslint-disable-next-line @typescript-eslint/no-empty-function
const AsyncFunctionConstructor = (async () => {}).constructor;

/**
* Computes the sum of a sample
* an async function check method only consider runtime support async syntax
*/
export const getSum = (samples: number[]) => samples.reduce((sum, n) => sum + n, 0);
export const isAsyncFunction = (fn: Fn) => fn.constructor === AsyncFunctionConstructor;

/**
* an async function check method consider runtime not support async syntax
*/
export const isAsyncFunctionDirty = (fn: Fn) => {
try {
const ret = fn();
return !!ret && typeof ret === 'object' && typeof ret.then === 'function';
} catch {
// if fn throw error directly, consider it's a sync function
return false;
}
};
5 changes: 4 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ test('basic', async () => {
expect(tasks[1]!.name).toEqual('bar');
expect(tasks[1]!.result!.totalTime).toBeGreaterThan(100);

expect(tasks[0]!.result!.hz * tasks[0]!.result!.period).toBeCloseTo(1);
// hz is ops/sec, period is ms unit value
expect(tasks[0]!.result!.hz * tasks[0]!.result!.period).toBeCloseTo(1000);
});

test('runs should be equal-more than time and iterations', async () => {
Expand All @@ -48,7 +49,9 @@ test('events order', async () => {
warmupTime: 0,
});
bench
// eslint-disable-next-line @typescript-eslint/no-empty-function
.add('foo', async () => {})
// eslint-disable-next-line @typescript-eslint/no-empty-function
.add('bar', async () => {})
.add('error', async () => {
throw new Error('fake');
Expand Down
10 changes: 10 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ export type Options = {
*/
now?: () => number;

/**
* decide use which way to check task's fn is a async function.
*
* when it's false, only compare function's constructor
* when is's true, run task's fn and check whether return a PromiseLike object
*
* @default false
*/
dirtyAsyncCheck?: boolean;

/**
* An AbortSignal for aborting the benchmark
*/
Expand Down

0 comments on commit bd15cf8

Please sign in to comment.