Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Aslemammad committed Apr 15, 2024
1 parent b026d42 commit f975545
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 55 deletions.
17 changes: 15 additions & 2 deletions src/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export default class Bench extends EventTarget {

_todos: Map<string, Task> = new Map();

_concurrencyLevel?: 'task' | 'bench';

_concurrencyLimit = Infinity;

signal?: AbortSignal;

throws: boolean;
Expand Down Expand Up @@ -67,7 +71,7 @@ export default class Bench extends EventTarget {
}
}

runTask(task: Task) {
private runTask(task: Task) {
if (this.signal?.aborted) return task;
return task.run();
}
Expand All @@ -78,6 +82,7 @@ export default class Bench extends EventTarget {
* Note: This method does not do any warmup. Call {@link warmup} for that.
*/
async run() {
console.log('here');
this.dispatchEvent(createBenchEvent('start'));
const values: Task[] = [];
for (const task of [...this._tasks.values()]) {
Expand All @@ -91,7 +96,15 @@ export default class Bench extends EventTarget {
* similar to the {@link run} method but runs concurrently rather than sequentially
* default limit is Infinity
*/
async runConcurrently(limit = Infinity) {
async runConcurrently(limit = Infinity, level: typeof this._concurrencyLevel = 'bench') {
this._concurrencyLimit = limit;
this._concurrencyLevel = level;

console.log('level', level);
if (level === 'task') {
return this.run();
}

this.dispatchEvent(createBenchEvent('start'));

const remainingTasks = [...this._tasks.values()];
Expand Down
65 changes: 45 additions & 20 deletions src/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export default class Task extends EventTarget {
}

private async loop(time: number, iterations: number): Promise<{ error?: unknown, samples?: number[] }> {
console.log(this.bench._concurrencyLevel);
const isConcurrent = this.bench._concurrencyLevel === 'task';
const concurrencyLimit = this.bench._concurrencyLimit;
let totalTime = 0; // ms
const samples: number[] = [];
if (this.opts.beforeAll != null) {
Expand All @@ -63,33 +66,54 @@ export default class Task extends EventTarget {
}
const isAsync = await isAsyncTask(this);

const executeTask = async () => {
if (this.opts.beforeEach != null) {
await this.opts.beforeEach.call(this);
}

let taskTime = 0;
if (isAsync) {
const taskStart = this.bench.now();
await this.fn.call(this);
taskTime = this.bench.now() - taskStart;
} else {
const taskStart = this.bench.now();
this.fn.call(this);
taskTime = this.bench.now() - taskStart;
}

samples.push(taskTime);
totalTime += taskTime;

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

try {
const currentTasks: Promise<void>[] = []; // only for task level concurrency
while (
(totalTime < time || samples.length < iterations)
(totalTime < time || ((samples.length + currentTasks.length) < iterations))
&& !this.bench.signal?.aborted
) {
if (this.opts.beforeEach != null) {
await this.opts.beforeEach.call(this);
}

let taskTime = 0;
if (isAsync) {
const taskStart = this.bench.now();
await this.fn.call(this);
taskTime = this.bench.now() - taskStart;
console.log('start', samples.length, currentTasks.length, iterations, isConcurrent, currentTasks.length, concurrencyLimit);
if (isConcurrent) {
if (currentTasks.length < concurrencyLimit) {
currentTasks.push(executeTask());
} else {
await Promise.all(currentTasks);
currentTasks.length = 0;
}
} else {
const taskStart = this.bench.now();
this.fn.call(this);
taskTime = this.bench.now() - taskStart;
}

samples.push(taskTime);
totalTime += taskTime;

if (this.opts.afterEach != null) {
await this.opts.afterEach.call(this);
// console.log('non concurrent')
await executeTask();
}
}
// The concurrencyLimit is Infinity
if (currentTasks.length) {
await Promise.all(currentTasks);
currentTasks.length = 0;
}
} catch (error) {
return { error };
}
Expand Down Expand Up @@ -236,6 +260,7 @@ export default class Task extends EventTarget {
*/
reset() {
this.dispatchEvent(createBenchEvent('reset', this));
console.log('reset');
this.runs = 0;
this.result = undefined;
}
Expand Down
64 changes: 32 additions & 32 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,38 +154,38 @@ test('events order', async () => {
expect(abortTask.result).toBeUndefined();
}, 10000);

test('events order 2', async () => {
const bench = new Bench({
warmupIterations: 0,
warmupTime: 0,
});

bench
.add('foo', async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
})
.add('bar', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
});

const events: string[] = [];

const fooTask = bench.getTask('foo')!;
const barTask = bench.getTask('bar')!;
fooTask.addEventListener('complete', () => {
events.push('foo-complete');
expect(events).not.toContain('bar-complete');
});

barTask.addEventListener('complete', () => {
events.push('bar-complete');
expect(events).toContain('foo-complete');
});

await bench.run();

await new Promise((resolve) => setTimeout(resolve, 150));
});
// test('events order 2', async () => {
// const bench = new Bench({
// warmupIterations: 0,
// warmupTime: 0,
// });

// bench
// .add('foo', async () => {
// await new Promise((resolve) => setTimeout(resolve, 50));
// })
// .add('bar', async () => {
// await new Promise((resolve) => setTimeout(resolve, 100));
// });

// const events: string[] = [];

// const fooTask = bench.getTask('foo')!;
// const barTask = bench.getTask('bar')!;
// fooTask.addEventListener('complete', () => {
// events.push('foo-complete');
// expect(events).not.toContain('bar-complete');
// });

// barTask.addEventListener('complete', () => {
// events.push('bar-complete');
// expect(events).toContain('foo-complete');
// });

// await bench.run();

// await new Promise((resolve) => setTimeout(resolve, 150));
// });

test('todo event', async () => {
const bench = new Bench({ time: 50 });
Expand Down
45 changes: 44 additions & 1 deletion test/sequential.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test('sequential', async () => {
expect(isFirstTaskDefined).toBe(true);
});

test('concurrent', async () => {
test('concurrent (bench level)', async () => {
const concurrentBench = new Bench({
time: 0,
iterations: 100,
Expand Down Expand Up @@ -62,3 +62,46 @@ test('concurrent', async () => {
expect(shouldNotBeDefinedFirst1!).toBeDefined();
expect(shouldNotBeDefinedFirst2!).toBeDefined();
});

test('concurrent (task level)', async () => {
console.log('here start');
const iterations = 10;
const concurrentBench = new Bench({
time: 0,
iterations,
});
const key = 'sample 1';

const runs = { value: 0 };
concurrentBench
.add(key, async () => {
runs.value++;
await setTimeout(10);
// all task function should be here after 10ms
console.log(runs.value, iterations);
expect(runs.value).toEqual(iterations);
await setTimeout(10);
});

await concurrentBench.run();
expect(concurrentBench.getTask(key)!.runs).toEqual(0);
for (const result of concurrentBench.results) {
expect(result?.error).toMatch(/AssertionError/);
}
concurrentBench.reset();
runs.value = 0;

await concurrentBench.runConcurrently();
expect(concurrentBench.getTask(key)!.runs).toEqual(0);
for (const result of concurrentBench.results) {
expect(result?.error).toMatch(/AssertionError/);
}
concurrentBench.reset();
runs.value = 0;

await concurrentBench.runConcurrently(Infinity, 'task');
expect(concurrentBench.getTask(key)!.runs).toEqual(10);
for (const result of concurrentBench.results) {
expect(result?.error).toBeUndefined();
}
});

0 comments on commit f975545

Please sign in to comment.