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.
205 lines
5.5 KiB
205 lines
5.5 KiB
10 years ago
|
/**
|
||
|
* Copyright 2013-2015, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*
|
||
|
* @providesModule ReactDefaultPerfAnalysis
|
||
|
*/
|
||
|
|
||
|
var assign = require("./Object.assign");
|
||
|
|
||
|
// Don't try to save users less than 1.2ms (a number I made up)
|
||
|
var DONT_CARE_THRESHOLD = 1.2;
|
||
|
var DOM_OPERATION_TYPES = {
|
||
|
'_mountImageIntoNode': 'set innerHTML',
|
||
|
INSERT_MARKUP: 'set innerHTML',
|
||
|
MOVE_EXISTING: 'move',
|
||
|
REMOVE_NODE: 'remove',
|
||
|
TEXT_CONTENT: 'set textContent',
|
||
|
'updatePropertyByID': 'update attribute',
|
||
|
'deletePropertyByID': 'delete attribute',
|
||
|
'updateStylesByID': 'update styles',
|
||
|
'updateInnerHTMLByID': 'set innerHTML',
|
||
|
'dangerouslyReplaceNodeWithMarkupByID': 'replace'
|
||
|
};
|
||
|
|
||
|
function getTotalTime(measurements) {
|
||
|
// TODO: return number of DOM ops? could be misleading.
|
||
|
// TODO: measure dropped frames after reconcile?
|
||
|
// TODO: log total time of each reconcile and the top-level component
|
||
|
// class that triggered it.
|
||
|
var totalTime = 0;
|
||
|
for (var i = 0; i < measurements.length; i++) {
|
||
|
var measurement = measurements[i];
|
||
|
totalTime += measurement.totalTime;
|
||
|
}
|
||
|
return totalTime;
|
||
|
}
|
||
|
|
||
|
function getDOMSummary(measurements) {
|
||
|
var items = [];
|
||
|
for (var i = 0; i < measurements.length; i++) {
|
||
|
var measurement = measurements[i];
|
||
|
var id;
|
||
|
|
||
|
for (id in measurement.writes) {
|
||
|
measurement.writes[id].forEach(function(write) {
|
||
|
items.push({
|
||
|
id: id,
|
||
|
type: DOM_OPERATION_TYPES[write.type] || write.type,
|
||
|
args: write.args
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
return items;
|
||
|
}
|
||
|
|
||
|
function getExclusiveSummary(measurements) {
|
||
|
var candidates = {};
|
||
|
var displayName;
|
||
|
|
||
|
for (var i = 0; i < measurements.length; i++) {
|
||
|
var measurement = measurements[i];
|
||
|
var allIDs = assign(
|
||
|
{},
|
||
|
measurement.exclusive,
|
||
|
measurement.inclusive
|
||
|
);
|
||
|
|
||
|
for (var id in allIDs) {
|
||
|
displayName = measurement.displayNames[id].current;
|
||
|
|
||
|
candidates[displayName] = candidates[displayName] || {
|
||
|
componentName: displayName,
|
||
|
inclusive: 0,
|
||
|
exclusive: 0,
|
||
|
render: 0,
|
||
|
count: 0
|
||
|
};
|
||
|
if (measurement.render[id]) {
|
||
|
candidates[displayName].render += measurement.render[id];
|
||
|
}
|
||
|
if (measurement.exclusive[id]) {
|
||
|
candidates[displayName].exclusive += measurement.exclusive[id];
|
||
|
}
|
||
|
if (measurement.inclusive[id]) {
|
||
|
candidates[displayName].inclusive += measurement.inclusive[id];
|
||
|
}
|
||
|
if (measurement.counts[id]) {
|
||
|
candidates[displayName].count += measurement.counts[id];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now make a sorted array with the results.
|
||
|
var arr = [];
|
||
|
for (displayName in candidates) {
|
||
|
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
|
||
|
arr.push(candidates[displayName]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arr.sort(function(a, b) {
|
||
|
return b.exclusive - a.exclusive;
|
||
|
});
|
||
|
|
||
|
return arr;
|
||
|
}
|
||
|
|
||
|
function getInclusiveSummary(measurements, onlyClean) {
|
||
|
var candidates = {};
|
||
|
var inclusiveKey;
|
||
|
|
||
|
for (var i = 0; i < measurements.length; i++) {
|
||
|
var measurement = measurements[i];
|
||
|
var allIDs = assign(
|
||
|
{},
|
||
|
measurement.exclusive,
|
||
|
measurement.inclusive
|
||
|
);
|
||
|
var cleanComponents;
|
||
|
|
||
|
if (onlyClean) {
|
||
|
cleanComponents = getUnchangedComponents(measurement);
|
||
|
}
|
||
|
|
||
|
for (var id in allIDs) {
|
||
|
if (onlyClean && !cleanComponents[id]) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var displayName = measurement.displayNames[id];
|
||
|
|
||
|
// Inclusive time is not useful for many components without knowing where
|
||
|
// they are instantiated. So we aggregate inclusive time with both the
|
||
|
// owner and current displayName as the key.
|
||
|
inclusiveKey = displayName.owner + ' > ' + displayName.current;
|
||
|
|
||
|
candidates[inclusiveKey] = candidates[inclusiveKey] || {
|
||
|
componentName: inclusiveKey,
|
||
|
time: 0,
|
||
|
count: 0
|
||
|
};
|
||
|
|
||
|
if (measurement.inclusive[id]) {
|
||
|
candidates[inclusiveKey].time += measurement.inclusive[id];
|
||
|
}
|
||
|
if (measurement.counts[id]) {
|
||
|
candidates[inclusiveKey].count += measurement.counts[id];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now make a sorted array with the results.
|
||
|
var arr = [];
|
||
|
for (inclusiveKey in candidates) {
|
||
|
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
|
||
|
arr.push(candidates[inclusiveKey]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arr.sort(function(a, b) {
|
||
|
return b.time - a.time;
|
||
|
});
|
||
|
|
||
|
return arr;
|
||
|
}
|
||
|
|
||
|
function getUnchangedComponents(measurement) {
|
||
|
// For a given reconcile, look at which components did not actually
|
||
|
// render anything to the DOM and return a mapping of their ID to
|
||
|
// the amount of time it took to render the entire subtree.
|
||
|
var cleanComponents = {};
|
||
|
var dirtyLeafIDs = Object.keys(measurement.writes);
|
||
|
var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
|
||
|
|
||
|
for (var id in allIDs) {
|
||
|
var isDirty = false;
|
||
|
// For each component that rendered, see if a component that triggered
|
||
|
// a DOM op is in its subtree.
|
||
|
for (var i = 0; i < dirtyLeafIDs.length; i++) {
|
||
|
if (dirtyLeafIDs[i].indexOf(id) === 0) {
|
||
|
isDirty = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!isDirty && measurement.counts[id] > 0) {
|
||
|
cleanComponents[id] = true;
|
||
|
}
|
||
|
}
|
||
|
return cleanComponents;
|
||
|
}
|
||
|
|
||
|
var ReactDefaultPerfAnalysis = {
|
||
|
getExclusiveSummary: getExclusiveSummary,
|
||
|
getInclusiveSummary: getInclusiveSummary,
|
||
|
getDOMSummary: getDOMSummary,
|
||
|
getTotalTime: getTotalTime
|
||
|
};
|
||
|
|
||
|
module.exports = ReactDefaultPerfAnalysis;
|