You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
8.2 KiB
244 lines
8.2 KiB
var Class = require('./Class'); |
|
var defaults = require('./defaults'); |
|
var Promise = require('./Promise'); |
|
var perfNow = require('./perfNow'); |
|
var delay = require('./delay'); |
|
var average = require('./average'); |
|
var reduce = require('./reduce'); |
|
var each = require('./each'); |
|
var map = require('./map'); |
|
var table = require('./table'); |
|
var toStr = require('./toStr'); |
|
exports = Class( |
|
{ |
|
initialize: function Benchmark(fn) { |
|
var options = |
|
arguments.length > 1 && arguments[1] !== undefined |
|
? arguments[1] |
|
: {}; |
|
defaults(options, defOpts); |
|
this._fn = fn; |
|
this._isRunning = false; |
|
this._options = options; |
|
}, |
|
run: function() { |
|
var _this = this; |
|
if (this._isRunning) { |
|
return this._pendingPromise; |
|
} |
|
this._reset(); |
|
this._isRunning = true; |
|
var options = this._options; |
|
var pendingPromise = new Promise(function(resolve, reject) { |
|
var runSample = function() { |
|
var initCount = |
|
arguments.length > 0 && arguments[0] !== undefined |
|
? arguments[0] |
|
: 1; |
|
delay(function() { |
|
_this |
|
._runSample(initCount) |
|
.then(function(_ref) { |
|
var period = _ref.period, |
|
count = _ref.count; |
|
var sample = _this._sample; |
|
sample.push(period); |
|
if ( |
|
perfNow() - _this._timeStamp < |
|
options.maxTime || |
|
sample.length < options.minSamples |
|
) { |
|
runSample(count); |
|
} else { |
|
resolve(_this._calcResult()); |
|
} |
|
}) |
|
.catch(function(err) { |
|
reject(err); |
|
}); |
|
}, options.delay); |
|
}; |
|
runSample(); |
|
}); |
|
function complete() { |
|
this._isRunning = false; |
|
delete this._pendingPromise; |
|
} |
|
pendingPromise.then(complete).catch(complete); |
|
this._pendingPromise = pendingPromise; |
|
return pendingPromise; |
|
}, |
|
_reset: function() { |
|
this._timeStamp = perfNow(); |
|
this._sample = []; |
|
}, |
|
_calcResult: function() { |
|
var sample = this._sample; |
|
var result = { |
|
sample: sample, |
|
toString: function() { |
|
var hz = this.hz, |
|
rme = this.rme, |
|
name = this.name; |
|
var size = this.sample.length; |
|
return '' |
|
.concat(name, ' x ') |
|
.concat( |
|
formatNumber(hz.toFixed(hz < 100 ? 2 : 0)), |
|
' ops/sec \xB1' |
|
) |
|
.concat(rme.toFixed(2), '% (') |
|
.concat(size, ' run') |
|
.concat(size === 1 ? '' : 's', ' sampled)'); |
|
} |
|
}; |
|
var size = sample.length; |
|
result.name = this._options.name || this._fn.name || 'anonymous'; |
|
result.mean = average.apply(null, sample); |
|
function varOf(sum, x) { |
|
return sum + Math.pow(x - result.mean, 2); |
|
} |
|
result.variance = reduce(sample, varOf, 0) / (size - 1) || 0; |
|
result.deviation = Math.sqrt(result.variance); |
|
result.sem = result.deviation / Math.sqrt(size); |
|
var critical = tTable[Math.round(size - 1) || 1] || tTable.infinity; |
|
result.moe = result.sem * critical; |
|
result.rme = (result.moe / result.mean) * 100 || 0; |
|
result.hz = 1000 / result.mean; |
|
return result; |
|
}, |
|
_runSample: function(count) { |
|
var _this2 = this; |
|
var options = this._options; |
|
var minTime = options.minTime; |
|
return new Promise(function(resolve, reject) { |
|
var runCycle = function(count) { |
|
delay(function() { |
|
var elapsed = 0; |
|
try { |
|
elapsed = _this2._runCycle(count); |
|
} catch (e) { |
|
return reject(e); |
|
} |
|
var period = elapsed / count; |
|
if (elapsed < minTime) { |
|
if (elapsed === 0) { |
|
count *= 100; |
|
} else { |
|
count += Math.ceil( |
|
(minTime - elapsed) / period |
|
); |
|
} |
|
runCycle(count); |
|
} else { |
|
resolve({ |
|
count: count, |
|
period: period |
|
}); |
|
} |
|
}, options.delay); |
|
}; |
|
runCycle(count); |
|
}); |
|
}, |
|
_runCycle: function(count) { |
|
var fn = this._fn; |
|
var now = perfNow(); |
|
while (count--) { |
|
fn(); |
|
} |
|
return perfNow() - now; |
|
} |
|
}, |
|
{ |
|
all: function(benches, options) { |
|
var promises = []; |
|
each(benches, function(bench) { |
|
if (!(bench instanceof exports)) { |
|
bench = new exports(bench, options); |
|
} |
|
promises.push(bench.run()); |
|
}); |
|
return Promise.all(promises).then(function(results) { |
|
results.toString = function() { |
|
var data = map(results, function(_ref2, idx) { |
|
var name = _ref2.name, |
|
sample = _ref2.sample, |
|
hz = _ref2.hz, |
|
rme = _ref2.rme; |
|
var columns = []; |
|
var size = sample.length; |
|
columns.push( |
|
toStr(idx + 1), |
|
name || 'anonymous', |
|
formatNumber(hz.toFixed(hz < 100 ? 2 : 0)), |
|
'\xB1'.concat(rme.toFixed(2), '%'), |
|
'' |
|
.concat(size, ' run') |
|
.concat(size === 1 ? '' : 's') |
|
); |
|
return columns; |
|
}); |
|
data.unshift([ |
|
'index', |
|
'name', |
|
'ops/sec', |
|
'rme', |
|
'sampled' |
|
]); |
|
return table(data); |
|
}; |
|
return results; |
|
}); |
|
} |
|
} |
|
); |
|
var defOpts = { |
|
minTime: 50, |
|
maxTime: 5000, |
|
minSamples: 5, |
|
delay: 5, |
|
name: '' |
|
}; |
|
var tTable = { |
|
'1': 12.706, |
|
'2': 4.303, |
|
'3': 3.182, |
|
'4': 2.776, |
|
'5': 2.571, |
|
'6': 2.447, |
|
'7': 2.365, |
|
'8': 2.306, |
|
'9': 2.262, |
|
'10': 2.228, |
|
'11': 2.201, |
|
'12': 2.179, |
|
'13': 2.16, |
|
'14': 2.145, |
|
'15': 2.131, |
|
'16': 2.12, |
|
'17': 2.11, |
|
'18': 2.101, |
|
'19': 2.093, |
|
'20': 2.086, |
|
'21': 2.08, |
|
'22': 2.074, |
|
'23': 2.069, |
|
'24': 2.064, |
|
'25': 2.06, |
|
'26': 2.056, |
|
'27': 2.052, |
|
'28': 2.048, |
|
'29': 2.045, |
|
'30': 2.042, |
|
infinity: 1.96 |
|
}; |
|
function formatNumber(number) { |
|
number = String(number).split('.'); |
|
return ( |
|
number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') + |
|
(number[1] ? '.' + number[1] : '') |
|
); |
|
} |
|
|
|
module.exports = exports;
|
|
|