wallet-core/thirdparty/systemjs/lib/perf.js

227 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SystemJS performance measurement addon
*
*/
(function() {
function perfEvent(eventName, eventItem) {
var evt = new ProfileEvent(eventName, eventItem);
events.push(evt);
return evt;
};
// we extend the hook function itself to create performance instrumentation per extension
var curHook = hook;
var resolverHooks = ['import', 'normalizeSync', 'decanonicalize', 'normalize'];
var pipelineHooks = ['locate', 'fetch', 'translate', 'instantiate'];
hook = function(name, newHook) {
// hook information
var resolver = resolverHooks.indexOf(name) != -1;
var pipeline = pipelineHooks.indexOf(name) != -1;
var hookEvtName = name + ':' + newHook.toString().substr(0, 200);
curHook(name, function(prevHook) {
return function() {
// this is called when the hook runs
var hookItem = resolver ? arguments[0] + ' : ' + arguments[1] + ' : ' + !!arguments[2] : arguments[0] && arguments[0].name;
var evt = perfEvent(hookEvtName, hookItem);
// ignore time spent in the old hook
var fn = newHook(function() {
evt.pause();
var output = prevHook.apply(this, arguments);
if (output && output.toString && output.toString() == '[object Promise]') {
return Promise.resolve(output)
.then(function(output) {
evt.resume();
return output;
})
.catch(function(err) {
evt.resume()
throw err;
});
}
else {
evt.resume();
return output;
}
});
var output;
try {
output = fn.apply(this, arguments);
}
catch(e) {
evt.cancel();
throw e;
}
if (output && output.toString && output.toString() == '[object Promise]') {
return Promise.resolve(output)
.then(function(output) {
evt.done();
return output;
})
.catch(function(err) {
evt.cancel();
throw err;
});
}
else {
evt.done();
return output;
}
};
});
};
// profiling events
var events = [];
var perf = typeof performance != 'undefined' : performance : Date;
// Performance Event class
function ProfileEvent(name, item) {
this.name = name;
this.item = (typeof item == 'function' ? item() : item) || 'default';
this.start = perf.now();
this.stop = null;
this.pauseTime = null;
this.cancelled = false;
}
ProfileEvent.prototype.rename = function(name, item) {
this.name = name;
if (arguments.length > 1)
this.item = item;
};
ProfileEvent.prototype.done = function() {
if (this.stop)
throw new TypeError('Event ' + this.name + ' (' + this.item + ') has already completed.');
this.stop = perf.now();
};
ProfileEvent.prototype.cancel = function() {
this.cancelled = true;
};
ProfileEvent.prototype.cancelIfNotDone = function() {
if (!this.stop)
this.cancelled = true;
};
ProfileEvent.prototype.pause = function() {
if (this.stop)
throw new TypeError('Event ' + this.name + ' (' + this.item + ') cannot be paused as it has finished.');
if (!this.pauseTime)
this.pauseTime = perf.now();
};
ProfileEvent.prototype.resume = function() {
if (!this.pauseTime)
throw new TypeError('Event ' + this.name + ' (' + this.item + ') is not already paused.');
this.start += perf.now() - this.pauseTime;
this.pauseTime = null;
};
var logged = false;
SystemJSLoader.prototype.perfSummary = function(includeEvts) {
logged = true;
// create groupings of events by event name to time data
// filtering out cancelled events
var groupedEvents = {};
events.forEach(function(evt) {
if (includeEvts && !includeEvts(evt))
return;
if (evt.cancelled)
return;
if (!evt.stop) {
console.warn('Event ' + evt.name + ' (' + evt.item + ') never completed.');
return;
}
var evtTimes = groupedEvents[evt.name] = groupedEvents[evt.name] || [];
evtTimes.push({
time: evt.stop - evt.start,
item: evt.item
});
});
Object.keys(groupedEvents).forEach(function(evt) {
var evtTimes = groupedEvents[evt];
// only one event -> log as a single event
if (evtTimes.length == 1) {
console.log(toTitleCase(evt) + (evtTimes[0].item != 'default' ? ' (' + evtTimes[0].item + ')' : ''));
logStat('Total Time', evtTimes[0].time);
console.log('');
return;
}
// multiple events, give some stats!
var evtCount = evtTimes.length;
console.log(toTitleCase(evt) + ' (' + evtCount + ' events)');
var totalTime = evtTimes.reduce(function(curSum, evt) {
return curSum + evt.time;
}, 0);
logStat('Cumulative Time', totalTime);
var mean = totalTime / evtCount;
logStat('Mean', mean);
var stdDev = Math.sqrt(evtTimes.reduce(function(curSum, evt) {
return curSum + Math.pow(evt.time - mean, 2);
}, 0) / evtCount);
logStat('Std Deviation', stdDev);
var withoutOutliers = evtTimes.filter(function(evt) {
return evt.time > mean - stdDev && evt.time < mean + stdDev;
});
logStat('Avg within 2σ', withoutOutliers.reduce(function(curSum, evt) {
return curSum + evt.time;
}, 0) / withoutOutliers.length);
var sorted = evtTimes.sort(function(a, b) {
return a.time > b.time ? 1 : -1;
});
var medianIndex = Math.round(evtCount / 2);
logStat('Median', sorted[medianIndex].time, sorted[medianIndex].evt);
logStat('Max', sorted[evtCount - 1].time, sorted[evtCount - 1].item);
logStat('Min', sorted[0].time, sorted[0].item);
var duplicates = evtTimes.filter(function(evt) {
return evtTimes.some(function(dup) {
return dup !== evt && dup.name == evt.name && dup.item == evt.item;
});
});
logStat('Duplicate Events', duplicates.length, true);
logStat('Total Duplicated Time', duplicates.reduce(function(duplicateTime, evt) {
return duplicateTime + evt.time;
}, 0));
console.log('');
});
};
function toTitleCase(title) {
return title.split('-').map(function(part) {
return part[0].toUpperCase() + part.substr(1);
}).join(' ');
}
var titleLen = 25;
function logStat(title, value, item, isNum) {
if (item === true) {
item = undefined;
isNum = true;
}
var spaces = Array(titleLen - title.length + 1).join(' ');
var value = isNum ? value : Math.round(value * 100) / 100 + 'ms';
console.log(' ' + title + spaces + ': ' + value + (item ? ' (' + item + ')' : ''));
}
})();