Skip to content

Commit 7ae8860

Browse files
Run the next BrowserStack benchmark immediately instead of in batches (#6717)
Before this change, the benchmark script would start x benchmarks, wait for them all to complete, and then start the next x. This change makes it start new benchmarks immediately when prior benchmarks complete so that there are always x benchmarks running. This doesn't work across tests that require different karma configs (e.g. changing model / parameters), and all tests from one karma config must complete before tests with a different karma config can start. Co-authored-by: Linchenn <[email protected]>
1 parent 6309136 commit 7ae8860

File tree

4 files changed

+114
-13
lines changed

4 files changed

+114
-13
lines changed

e2e/benchmarks/browserstack-benchmark/app.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const {
2929
firebaseConfig,
3030
endFirebaseInstance
3131
} = require('./firestore.js');
32+
const {PromiseQueue} = require('./promise_queue');
3233

3334
const port = process.env.PORT || 8001;
3435
let io;
@@ -160,22 +161,18 @@ async function benchmark(config, runOneBenchmark = getOneBenchmarkResult) {
160161
`dependencies...`);
161162
}
162163

164+
const promiseQueue = new PromiseQueue(cliArgs?.maxBenchmarks ?? 9);
163165
const results = [];
164-
let numActiveBenchmarks = 0;
166+
165167
// Runs and gets result of each queued benchmark
166168
for (const tabId in config.browsers) {
167-
numActiveBenchmarks++;
168-
results.push(runOneBenchmark(tabId, cliArgs?.maxTries).then((value) => {
169-
value.deviceInfo = config.browsers[tabId];
170-
value.modelInfo = config.benchmark;
171-
return value;
169+
results.push(promiseQueue.add(() => {
170+
return runOneBenchmark(tabId, cliArgs?.maxTries).then((value) => {
171+
value.deviceInfo = config.browsers[tabId];
172+
value.modelInfo = config.benchmark;
173+
return value;
174+
});
172175
}));
173-
174-
// Waits for specified # of benchmarks to complete before running more
175-
if (cliArgs?.maxBenchmarks && numActiveBenchmarks >= cliArgs.maxBenchmarks) {
176-
numActiveBenchmarks = 0;
177-
await Promise.allSettled(results);
178-
}
179176
}
180177

181178
// Optionally written to an outfile or pushed to a database once all

e2e/benchmarks/browserstack-benchmark/app_node_test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
runFirestore,
1010
firebaseConfig
1111
} = require('./firestore.js');
12+
const {PromiseQueue} = require('./promise_queue.js');
1213

1314
describe('test app.js cli', () => {
1415
const filePath = './benchmark_test_results.json';
@@ -257,3 +258,75 @@ describe('test adding to firestore', () => {
257258
}
258259
});
259260
});
261+
262+
function sleep(n) {
263+
return new Promise((resolve) => {
264+
setTimeout(() => { resolve(); }, n);
265+
});
266+
}
267+
268+
describe('promise queue', () => {
269+
let queue;
270+
beforeEach(() => {
271+
queue = new PromiseQueue(3);
272+
jasmine.clock().install();
273+
});
274+
275+
afterEach(() => {
276+
jasmine.clock().uninstall();
277+
});
278+
279+
it('runs a given number of functions at once', async () => {
280+
let promises = [];
281+
let started = [false, false, false, false, false];
282+
let resolved = [false, false, false, false, false];
283+
for (let i = 0; i < 5; i++) {
284+
resolved[i] = false;
285+
promises.push(queue.add(async () => {
286+
started[i] = true;
287+
await sleep((i + 1) * 10);
288+
resolved[i] = true;
289+
}));
290+
}
291+
292+
// Queue should immediately start 3 promises.
293+
expect(started).toEqual(
294+
[true, true, true, false, false]
295+
);
296+
expect(resolved).toEqual(
297+
[false, false, false, false, false]
298+
);
299+
300+
// After the first promise is done, queue should start the fourth one.
301+
jasmine.clock().tick(15);
302+
await promises[0];
303+
expect(started).toEqual(
304+
[true, true, true, true, false]
305+
);
306+
expect(resolved).toEqual(
307+
[true, false, false, false, false]
308+
);
309+
310+
// All running promises should finish, and the last should start.
311+
jasmine.clock().tick(1000);
312+
await promises[1];
313+
await promises[2];
314+
await promises[3];
315+
expect(started).toEqual(
316+
[true, true, true, true, true]
317+
);
318+
expect(resolved).toEqual(
319+
[true, true, true, true, false]
320+
);
321+
322+
// The last promise should finish
323+
jasmine.clock().tick(1000);
324+
await promises[4];
325+
expect(started).toEqual(
326+
[true, true, true, true, true]
327+
);
328+
expect(resolved).toEqual(
329+
[true, true, true, true, true]
330+
);
331+
});
332+
});

e2e/benchmarks/browserstack-benchmark/karma.conf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ module.exports = function(config) {
6363
'tfjs-automl/dist/tf-automl.js',
6464
].map(url => {
6565
let name = url.split('/')[0].replace('tfjs-', '').replace('backend-', '');
66-
if (config.localBuild.includes(name)) {
66+
if (config.localBuild?.includes(name)) {
6767
return `../../../dist/bin/${url}`;
6868
} else {
6969
return `https://unpkg.com/@tensorflow/${url.replace('/', '@latest/')}`;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class PromiseQueue {
2+
queue = [];
3+
running = 0;
4+
5+
constructor(concurrency = Infinity) {
6+
this.concurrency = concurrency;
7+
}
8+
9+
add(func) {
10+
return new Promise((resolve, reject) => {
11+
this.queue.push(async () => {
12+
this.running++;
13+
try {
14+
resolve(await func());
15+
} finally {
16+
this.running--;
17+
this.run();
18+
}
19+
});
20+
this.run();
21+
});
22+
}
23+
24+
run() {
25+
while (this.running < this.concurrency && this.queue.length > 0) {
26+
this.queue.shift()();
27+
}
28+
}
29+
}
30+
31+
module.exports = {PromiseQueue};

0 commit comments

Comments
 (0)