Skip to content
This repository has been archived by the owner on Apr 11, 2024. It is now read-only.

Allow Asynchronous Calls in setup()/teardown() Functions #174

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
103 changes: 81 additions & 22 deletions benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,16 +720,46 @@

if (bench.aborted) {
// cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete.
deferred.teardown();
clone.running = false;
cycle(deferred);
deferred.teardown(deferred);
}
else if (++deferred.cycles < clone.count) {
clone.compiled.call(deferred, context, timer);
}
else {
timer.stop(deferred);
deferred.teardown();
deferred.teardown(deferred);
}
}

/*------------------------------------------------------------------------*/

/**
* Handles completing the deferred setup function.
*
* @memberOf Benchmark.Deferred
*/
function suResolve() {
// This is a placeholder function. The actual function is generated with
// each benchmark.
}

/*------------------------------------------------------------------------*/

/**
* Handles completing the deferred teardown function.
*
* @memberOf Benchmark.Deferred
*/
function tdResolve() {
var deferred = this,
clone = deferred.benchmark,
bench = clone._original;

if (bench.aborted) {
clone.running = false;
cycle(deferred);
}
else {
delay(clone, function() { cycle(deferred); });
}
}
Expand Down Expand Up @@ -1589,16 +1619,17 @@
// When `deferred.cycles` is `0` then...
'if(!d#.cycles){' +
// set `deferred.fn`,
'd#.fn=function(){var ${fnArg}=d#;if(typeof f#=="function"){try{${fn}\n}catch(e#){f#(d#)}}else{${fn}\n}};' +
'd#.fn=function(){var ${fnArg}=d#;if(typeof f#=="function"){try{${fn}\n}catch(e#){f#(d#);}}else{${fn}\n}};' +
// set `deferred.teardown`,
'd#.teardown=function(){d#.cycles=0;if(typeof td#=="function"){try{${teardown}\n}catch(e#){td#()}}else{${teardown}\n}};' +
// execute the benchmark's `setup`,
'if(typeof su#=="function"){try{${setup}\n}catch(e#){su#()}}else{${setup}\n};' +
// start timer,
't#.start(d#);' +
// and then execute `deferred.fn` and return a dummy object.
'}d#.fn();return{uid:"${uid}"}'

'd#.teardown=function(){d#.cycles=0;var ${fnArg}=d#;if(typeof td#=="function"){try{${teardown}\n}catch(e#){td#(d#);}}else{${teardown}\n}};' +
// generate setup resolve function
'd#.suResolve=function(){t#.start(d#);d#.fn();};' +
// execute the benchmark's `setup` with `deferred.suResolve` executing `deferred.fn`
'if(typeof su#=="function"){try{${setup}\n}catch(e#){su#(d#);}}else{${setup}\n}' +
// When `deferred.cycles` is not `0` then just execute `deferred.fn`
'}else{d#.fn();}' +
// and return a dummy object.
'return{uid:"${uid}"};'
: 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count,n#=t#.ns;${setup}\n${begin};' +
'while(i#--){${fn}\n}${end};${teardown}\nreturn{elapsed:r#,uid:"${uid}"}';

Expand Down Expand Up @@ -1663,17 +1694,44 @@
* Creates a compiled function from the given function `body`.
*/
function createCompiled(bench, decompilable, deferred, body) {
var fn = bench.fn,
fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '';
var setup = bench.setup,
fn = bench.fn,
teardown = bench.teardown,
suArg = deferred ? getFirstArgument(setup) : '',
fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '',
tdArg = deferred ? getFirstArgument(teardown) : '';

templateData.uid = uid + uidCounter++;

_.assign(templateData, {
'setup': decompilable ? getSource(bench.setup) : interpolate('m#.setup()'),
'fn': decompilable ? getSource(fn) : interpolate('m#.fn(' + fnArg + ')'),
'fnArg': fnArg,
'teardown': decompilable ? getSource(bench.teardown) : interpolate('m#.teardown()')
});
if (deferred) {
if (decompilable) {
var suSource = getSource(setup),
tdSource = getSource(teardown);

_.assign(templateData, {
'setup': (suSource == '' || suSource == '// No operation performed.') ? interpolate('d#.suResolve();') : getSource(setup),
'fn': getSource(fn),
'fnArg': fnArg,
'teardown': (tdSource == '' || tdSource == '// No operation performed.') ? interpolate('d#.tdResolve();') : getSource(teardown)
});
}
else {
_.assign(templateData, {
'setup': suArg ? interpolate('m#.setup(' + suArg + ');') : interpolate('d#.suResolve();'),
'fn': interpolate('m#.fn(' + fnArg + ');'),
'fnArg': fnArg,
'teardown': tdArg ? interpolate('m#.teardown(' + tdArg + ');') : interpolate('d#.tdResolve();')
});
}
}
else {
_.assign(templateData, {
'setup': decompilable ? getSource(setup) : interpolate('m#.setup()'),
'fn': decompilable ? getSource(fn) : interpolate('m#.fn(' + fnArg + ')'),
'fnArg': fnArg,
'teardown': decompilable ? getSource(teardown) : interpolate('m#.teardown()')
});
}

// Use API of chosen timer.
if (timer.unit == 'ns') {
Expand Down Expand Up @@ -2605,7 +2663,8 @@
});

_.assign(Deferred.prototype, {
'resolve': resolve
'resolve': resolve,
'tdResolve': tdResolve
});

/*------------------------------------------------------------------------*/
Expand Down
27 changes: 23 additions & 4 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1229,14 +1229,31 @@
.run();
});

QUnit.test('should run a deferred benchmark correctly without being asynchronous', function(assert) {
var done = assert.async();

Benchmark(function(deferred) {
setTimeout(function() { deferred.resolve(); }, 1e3);
}, {
'defer': true,
'setup': function(deferred) { deferred.suResolve(); },
'teardown': function(deferred) { deferred.tdResolve(); },
'onComplete': function() {
assert.strictEqual(this.hz.toFixed(0), '1');
done();
}
})
.run();
});

QUnit.test('should run with string values for "fn", "setup", and "teardown"', function(assert) {
var done = assert.async();

Benchmark({
'defer': true,
'setup': 'var x = [3, 2, 1];',
'setup': 'var x = [3, 2, 1]; setTimeout(function() { deferred.suResolve(); }, 10);',
'fn': 'setTimeout(function() { x.sort(); deferred.resolve(); }, 10);',
'teardown': 'x.length = 0;',
'teardown': 'x.length = 0; setTimeout(function() { deferred.tdResolve(); }, 10);',
'onComplete': function() {
assert.ok(true);
done();
Expand All @@ -1252,15 +1269,17 @@

Benchmark({
'defer': true,
'setup': function() {
'setup': function(deferred) {
fired.push('setup');
setTimeout(function() { deferred.suResolve(); }, 10);
},
'fn': function(deferred) {
fired.push('fn');
setTimeout(function() { deferred.resolve(); }, 10);
},
'teardown': function() {
'teardown': function(deferred) {
fired.push('teardown');
setTimeout(function() { deferred.tdResolve(); }, 10);
},
'onComplete': function() {
var actual = fired.join().replace(/(fn,)+/g, '$1').replace(/(setup,fn,teardown(?:,|$))+/, '$1');
Expand Down