296 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			9.6 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));
 |