285 lines
9.9 KiB
JavaScript
285 lines
9.9 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
(function(_global) {
|
|
var allTasks = _global['__zone_symbol__performance_tasks'];
|
|
if (!allTasks) {
|
|
allTasks = _global['__zone_symbol__performance_tasks'] = [];
|
|
}
|
|
|
|
var mark = _global['__zone_symbol__mark'] = function(name) {
|
|
performance && performance['mark'] && performance['mark'](name);
|
|
};
|
|
|
|
var measure = _global['__zone_symbol__measure'] = function(name, label) {
|
|
performance && performance['measure'] && performance['measure'](name, label);
|
|
};
|
|
|
|
var getEntries = _global['__zone_symbol__getEntries'] = function() {
|
|
performance && performance['getEntries'] && performance['getEntries']();
|
|
};
|
|
|
|
var getEntriesByName = _global['__zone_symbol__getEntriesByName'] = function(name) {
|
|
return performance && performance['getEntriesByName'] && performance['getEntriesByName'](name);
|
|
};
|
|
|
|
var clearMarks = _global['__zone_symbol__clearMarks'] = function(name) {
|
|
return performance && performance['clearMarks'] && performance['clearMarks'](name);
|
|
};
|
|
|
|
var clearMeasures = _global['__zone_symbol__clearMeasures'] = function(name) {
|
|
return performance && performance['clearMeasures'] && performance['clearMeasures'](name);
|
|
};
|
|
|
|
var averageMeasures = _global['__zone_symbol__averageMeasures'] = function(name, times) {
|
|
var sum = _global['__zone_symbol__getEntriesByName'](name)
|
|
.filter(function(m) { return m.entryType === 'measure'; })
|
|
.map(function(m) { return m.duration })
|
|
.reduce(function(sum, d) { return sum + d; });
|
|
return sum / times;
|
|
};
|
|
|
|
var serialPromise = _global['__zone_symbol__serialPromise'] =
|
|
function(promiseFactories) {
|
|
let lastPromise;
|
|
for (var i = 0; i < promiseFactories.length; i++) {
|
|
var promiseFactory = promiseFactories[i];
|
|
if (!lastPromise) {
|
|
lastPromise = promiseFactory.factory(promiseFactory.context).then(function(value) {
|
|
return {value, idx: 0};
|
|
});
|
|
} else {
|
|
lastPromise = lastPromise.then(function(ctx) {
|
|
var idx = ctx.idx + 1;
|
|
var promiseFactory = promiseFactories[idx];
|
|
return promiseFactory.factory(promiseFactory.context).then(function(value) {
|
|
return {value, idx};
|
|
});
|
|
});
|
|
}
|
|
}
|
|
return lastPromise;
|
|
}
|
|
|
|
var callbackContext = _global['__zone_symbol__callbackContext'] = {};
|
|
var zone = _global['__zone_symbol__callbackZone'] = Zone.current.fork({
|
|
name: 'callback',
|
|
onScheduleTask: function(delegate, curr, target, task) {
|
|
delegate.scheduleTask(target, task);
|
|
if (task.type === callbackContext.type &&
|
|
task.source.indexOf(callbackContext.source) !== -1) {
|
|
if (task.type === 'macroTask' || task.type === 'eventTask') {
|
|
var invoke = task.invoke;
|
|
task.invoke = function() {
|
|
mark(callbackContext.measureName);
|
|
var result = invoke.apply(this, arguments);
|
|
measure(callbackContext.measureName, callbackContext.measureName);
|
|
return result;
|
|
};
|
|
} else if (task.type === 'microTask') {
|
|
var callback = task.callback;
|
|
task.callback = function() {
|
|
mark(callbackContext.measureName);
|
|
var result = callback.apply(this, arguments);
|
|
measure(callbackContext.measureName, callbackContext.measureName);
|
|
return result;
|
|
};
|
|
}
|
|
}
|
|
return task;
|
|
}
|
|
});
|
|
|
|
var runAsync = _global['__zone_symbol__runAsync'] = function(testFn, times, _delay) {
|
|
var delay = _delay | 100;
|
|
const fnPromise = function() {
|
|
return new Promise(function(res, rej) {
|
|
// run test with a setTimeout
|
|
// several times to decrease measurement error
|
|
setTimeout(function() { testFn().then(function() { res(); }); }, delay);
|
|
});
|
|
};
|
|
var promiseFactories = [];
|
|
for (var i = 0; i < times; i++) {
|
|
promiseFactories.push({factory: fnPromise, context: {}});
|
|
}
|
|
|
|
return serialPromise(promiseFactories);
|
|
};
|
|
|
|
var getNativeMethodName = function(nativeWithSymbol) {
|
|
return nativeWithSymbol.replace('__zone_symbol__', 'native_');
|
|
};
|
|
|
|
function testAddRemove(api, count) {
|
|
var timerId = [];
|
|
|
|
var name = api.method;
|
|
mark(name);
|
|
for (var i = 0; i < count; i++) {
|
|
timerId.push(api.run());
|
|
}
|
|
measure(name, name);
|
|
|
|
if (api.supportClear) {
|
|
var clearName = api.clearMethod;
|
|
mark(clearName);
|
|
for (var i = 0; i < count; i++) {
|
|
api.runClear(timerId[i]);
|
|
}
|
|
measure(clearName, clearName);
|
|
}
|
|
|
|
timerId = [];
|
|
|
|
var nativeName = getNativeMethodName(api.nativeMethod);
|
|
mark(nativeName);
|
|
for (var i = 0; i < count; i++) {
|
|
timerId.push(api.nativeRun());
|
|
}
|
|
measure(nativeName, nativeName);
|
|
|
|
if (api.supportClear) {
|
|
var nativeClearName = getNativeMethodName(api.nativeClearMethod);
|
|
mark(nativeClearName);
|
|
for (var i = 0; i < count; i++) {
|
|
api.nativeRunClear(timerId[i]);
|
|
}
|
|
measure(nativeClearName, nativeClearName);
|
|
}
|
|
|
|
return Promise.resolve(1);
|
|
}
|
|
|
|
function testCallback(api, count) {
|
|
var promises = [Promise.resolve(1)];
|
|
for (var i = 0; i < count; i++) {
|
|
var r = api.run();
|
|
if (api.isAsync) {
|
|
promises.push(r);
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < count; i++) {
|
|
var r = api.nativeRun();
|
|
if (api.isAsync) {
|
|
promises.push(r);
|
|
}
|
|
}
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
function measureCallback(api, ops) {
|
|
var times = ops.times;
|
|
var displayText = ops.displayText;
|
|
var rawData = ops.rawData;
|
|
var summary = ops.summary;
|
|
|
|
var name = api.method;
|
|
var nativeName = getNativeMethodName(api.nativeMethod);
|
|
var measure = averageMeasures(name, times);
|
|
var nativeMeasure = averageMeasures(nativeName, times);
|
|
displayText += `- ${name} costs ${measure} ms\n`;
|
|
displayText += `- ${nativeName} costs ${nativeMeasure} ms\n`;
|
|
var absolute = Math.floor(1000 * (measure - nativeMeasure)) / 1000;
|
|
displayText += `# ${name} is ${absolute}ms slower than ${nativeName}\n`;
|
|
rawData[name + '_measure'] = measure;
|
|
rawData[nativeName + '_measure'] = nativeMeasure;
|
|
summary[name] = absolute + 'ms';
|
|
}
|
|
|
|
function measureAddRemove(api, ops) {
|
|
var times = ops.times;
|
|
var displayText = ops.displayText;
|
|
var rawData = ops.rawData;
|
|
var summary = ops.summary;
|
|
|
|
var name = api.method;
|
|
var nativeName = getNativeMethodName(api.nativeMethod);
|
|
|
|
var measure = averageMeasures(name, times);
|
|
var nativeMeasure = averageMeasures(nativeName, times);
|
|
displayText += `- ${name} costs ${measure} ms\n`;
|
|
displayText += `- ${nativeName} costs ${nativeMeasure} ms\n`;
|
|
var percent = Math.floor(100 * (measure - nativeMeasure) / nativeMeasure);
|
|
displayText += `# ${name} is ${percent}% slower than ${nativeName}\n`;
|
|
rawData[name + '_measure'] = measure;
|
|
rawData[nativeName + '_measure'] = nativeMeasure;
|
|
summary[name] = percent + '%';
|
|
if (api.supportClear) {
|
|
var clearName = api.clearMethod;
|
|
var nativeClearName = getNativeMethodName(api.nativeClearMethod);
|
|
var clearMeasure = averageMeasures(clearName, times);
|
|
var nativeClearMeasure = averageMeasures(nativeClearName, times);
|
|
var clearPercent = Math.floor(100 * (clearMeasure - nativeClearMeasure) / nativeClearMeasure);
|
|
displayText += `- ${clearName} costs ${clearMeasure} ms\n`;
|
|
displayText += `- ${nativeClearName} costs ${nativeClearMeasure} ms\n`;
|
|
displayText += `# ${clearName} is ${clearPercent}% slower than ${nativeClearName}\n`;
|
|
rawData[clearName + '_measure'] = clearMeasure;
|
|
rawData[nativeClearName + '_measure'] = nativeClearMeasure;
|
|
summary[clearName] = clearPercent + '%';
|
|
}
|
|
}
|
|
|
|
var testRunner = _global['__zone_symbol__testRunner'] = function(testTarget) {
|
|
var title = testTarget.title;
|
|
var apis = testTarget.apis;
|
|
var methods = apis.reduce(function(acc, api) {
|
|
return acc.concat([
|
|
api.method, api.nativeMethod
|
|
].concat(api.supportClear ? [api.clearMethod, api.nativeClearMethod] : [])
|
|
.concat[api.method + '_callback', api.nativeMethod + '_callback']);
|
|
|
|
}, []);
|
|
var times = testTarget.times;
|
|
|
|
allTasks.push({
|
|
title: title,
|
|
cleanFn: function() {
|
|
methods.forEach(function(m) {
|
|
clearMarks(m);
|
|
clearMeasures(m);
|
|
});
|
|
},
|
|
before: function() { testTarget.before && testTarget.before(); },
|
|
after: function() { testTarget.after && testTarget.after(); },
|
|
testFn: function() {
|
|
var count = typeof testTarget.count === 'number' ? testTarget.count : 10000;
|
|
var times = typeof testTarget.times === 'number' ? testTarget.times : 5;
|
|
|
|
var testFunction = function() {
|
|
var promises = [];
|
|
apis.forEach(function(api) {
|
|
if (api.isCallback) {
|
|
var r = testCallback(api, count / 100);
|
|
promises.push(api.isAsync ? r : Promise.resolve(1));
|
|
} else {
|
|
var r = testAddRemove(api, count);
|
|
promises.push[api.isAsync ? r : Promise.resolve(1)];
|
|
}
|
|
});
|
|
return Promise.all(promises);
|
|
};
|
|
|
|
return runAsync(testFunction, times).then(function() {
|
|
var displayText = `running ${count} times\n`;
|
|
var rawData = {};
|
|
var summary = {};
|
|
apis.forEach(function(api) {
|
|
if (api.isCallback) {
|
|
measureCallback(api, {times, displayText, rawData, summary});
|
|
} else {
|
|
measureAddRemove(api, {times, displayText, rawData, summary});
|
|
}
|
|
});
|
|
return Promise.resolve({displayText: displayText, rawData: rawData, summary: summary});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
}(typeof window === 'undefined' ? global : window));
|