Skip to content

Commit

Permalink
fix: accurate results and hooks fix (#43)
Browse files Browse the repository at this point in the history
* update

* update

* update
  • Loading branch information
Aslemammad committed Jun 19, 2023
1 parent 30234c5 commit a0e3c9c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 36 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,15 @@ export type BenchEvent = Event & {
};
```

### `process.hrtime`
if you want more accurate results for nodejs with `process.hrtime`, then import
the `hrtimeNow` function from the library and pass it to the `Bench` options.

```ts
import { hrtimeNow } from 'tinybench';
```
It may make your benchmarks slower, check #42.

## Prior art

- [Benchmark.js](https://github.com/bestiejs/benchmark.js)
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type {
BenchEvent,
} from '../types';

export { now } from './utils';
export { now, hrtimeNow } from './utils';

export { Bench, Task };
export default Bench;
70 changes: 47 additions & 23 deletions src/task.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type {
Fn, TaskEvents, TaskResult, TaskEventsMap, FnOptions,
Fn,
TaskEvents,
TaskResult,
TaskEventsMap,
FnOptions,
} from '../types/index';
import Bench from './bench';
import tTable from './constants';
Expand Down Expand Up @@ -61,35 +65,33 @@ export default class Task extends EventTarget {
await this.opts.beforeAll.call(this);
}

while (
(totalTime < this.bench.time || this.runs < this.bench.iterations)
&& !this.bench.signal?.aborted
) {
if (this.opts.beforeEach != null) {
await this.opts.beforeEach.call(this);
}

let taskStart = 0;
try {
while (
(totalTime < this.bench.time || this.runs < this.bench.iterations)
&& !this.bench.signal?.aborted
) {
if (this.opts.beforeEach != null) {
await this.opts.beforeEach.call(this);
}

try {
taskStart = this.bench.now();
const taskStart = this.bench.now();
if (isAsync) {
await this.fn();
} else {
this.fn();
}
} catch (e) {
this.setResult({ error: e });
}
const taskTime = this.bench.now() - taskStart;

const taskTime = this.bench.now() - taskStart;
this.runs += 1;
samples.push(taskTime);
totalTime += taskTime;
samples.push(taskTime);
this.runs += 1;
totalTime += taskTime;

if (this.opts.afterEach != null) {
await this.opts.afterEach.call(this);
if (this.opts.afterEach != null) {
await this.opts.afterEach.call(this);
}
}
} catch (e) {
this.setResult({ error: e });
}

if (this.opts.afterAll != null) {
Expand All @@ -100,7 +102,7 @@ export default class Task extends EventTarget {

samples.sort((a, b) => a - b);

{
if (!this.result?.error) {
const min = samples[0]!;
const max = samples[samples.length - 1]!;
const period = totalTime / this.runs;
Expand Down Expand Up @@ -169,24 +171,46 @@ export default class Task extends EventTarget {
*/
async warmup() {
this.dispatchEvent(createBenchEvent('warmup', this));
const isAsync = isAsyncFunction(this.fn);
const startTime = this.bench.now();
let totalTime = 0;

await this.bench.setup(this, 'warmup');

if (this.opts.beforeAll != null) {
await this.opts.beforeAll.call(this);
}

while (
(totalTime < this.bench.warmupTime
|| this.runs < this.bench.warmupIterations)
&& !this.bench.signal?.aborted
) {
if (this.opts.beforeEach != null) {
await this.opts.beforeEach.call(this);
}

try {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve().then(this.fn);
if (isAsync) {
await this.fn();
} else {
this.fn();
}
} catch {
// todo
}

this.runs += 1;
totalTime = this.bench.now() - startTime;

if (this.opts.afterEach != null) {
await this.opts.afterEach.call(this);
}
}

if (this.opts.afterAll != null) {
await this.opts.afterAll.call(this);
}
this.bench.teardown(this, 'warmup');

Expand Down
9 changes: 3 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ import type { Fn } from '../types/index';

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

export const now = () => {
if (typeof globalThis.process?.hrtime === 'function') {
return nanoToMs(Number(process.hrtime.bigint()));
}
return performance.now();
};
export const hrtimeNow = () => nanoToMs(Number(process.hrtime.bigint()));

export const now = () => performance.now();

/**
* Computes the arithmetic mean of a sample.
Expand Down
14 changes: 8 additions & 6 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,10 @@ test('setup and teardown', async () => {
});

test('task beforeAll, afterAll, beforeEach, afterEach', async () => {
const bench = new Bench({ time: 50 });
const iterations = 100;
const bench = new Bench({
time: 0, warmupTime: 0, iterations, warmupIterations: iterations,
});

const beforeAll = vi.fn(function hook(this: Task) {
expect(this).toBe(bench.getTask('foo'));
Expand All @@ -303,14 +306,13 @@ test('task beforeAll, afterAll, beforeEach, afterEach', async () => {
beforeEach,
afterEach,
});
const task = bench.getTask('foo')!;

await bench.warmup();
await bench.run();

expect(beforeAll.mock.calls.length).toBe(1);
expect(afterAll.mock.calls.length).toBe(1);
expect(beforeEach.mock.calls.length).toBe(task.runs);
expect(afterEach.mock.calls.length).toBe(task.runs);
expect(beforeAll.mock.calls.length).toBe(2);
expect(afterAll.mock.calls.length).toBe(2);
expect(beforeEach.mock.calls.length).toBe(iterations * 2 /* warmup + run */);
expect(afterEach.mock.calls.length).toBe(iterations * 2);
expect(beforeEach.mock.calls.length).toBe(afterEach.mock.calls.length);
});

0 comments on commit a0e3c9c

Please sign in to comment.