Skip to content

Commit

Permalink
Stop relying on hierarchical IDs in ReactDefaultPerf
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiebits committed Nov 4, 2015
1 parent 5d94d7d commit 663c4b7
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 23 deletions.
4 changes: 2 additions & 2 deletions src/renderers/dom/shared/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -1180,9 +1180,9 @@ ReactDOMComponent.Mixin = {

};

ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
mountComponent: 'mountComponent',
updateComponent: 'updateComponent',
receiveComponent: 'receiveComponent',
});

assign(
Expand Down
10 changes: 10 additions & 0 deletions src/renderers/dom/shared/ReactDOMTextComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactPerf = require('ReactPerf');

var assign = require('Object.assign');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
Expand Down Expand Up @@ -153,4 +154,13 @@ assign(ReactDOMTextComponent.prototype, {

});

ReactPerf.measureMethods(
ReactDOMTextComponent.prototype,
'ReactDOMTextComponent',
{
mountComponent: 'mountComponent',
receiveComponent: 'receiveComponent',
}
);

module.exports = ReactDOMTextComponent;
55 changes: 42 additions & 13 deletions src/test/ReactDefaultPerf.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,26 @@ function addValue(obj, key, val) {
obj[key] = (obj[key] || 0) + val;
}

// Composites don't have any built-in ID: we have to make our own
var compositeIDMap;
var compositeIDCounter = 17000;
function getIDOfComposite(inst) {
if (!compositeIDMap) {
compositeIDMap = new WeakMap();
}
if (compositeIDMap.has(inst)) {
return compositeIDMap.get(inst);
} else {
var id = compositeIDCounter++;
compositeIDMap.set(inst, id);
return id;
}
}

var ReactDefaultPerf = {
_allMeasurements: [], // last item in the list is the current one
_mountStack: [0],
_compositeStack: [],
_injected: false,

start: function() {
Expand Down Expand Up @@ -125,10 +142,10 @@ var ReactDefaultPerf = {

_recordWrite: function(id, fnName, totalTime, args) {
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
var writes =
var entry =
ReactDefaultPerf
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]
.writes;
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
var writes = entry.writes;
writes[id] = writes[id] || [];
writes[id].push({
type: fnName,
Expand All @@ -143,27 +160,30 @@ var ReactDefaultPerf = {
var rv;
var start;

var entry = ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
];

if (fnName === '_renderNewRootComponent' ||
fnName === 'flushBatchedUpdates') {
// A "measurement" is a set of metrics recorded for each flush. We want
// to group the metrics for a given flush together so we can look at the
// components that rendered and the DOM operations that actually
// happened to determine the amount of "wasted work" performed.
ReactDefaultPerf._allMeasurements.push({
ReactDefaultPerf._allMeasurements.push(entry = {
exclusive: {},
inclusive: {},
render: {},
counts: {},
writes: {},
displayNames: {},
hierarchy: {},
totalTime: 0,
created: {},
});
start = performanceNow();
rv = func.apply(this, args);
ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
].totalTime = performanceNow() - start;
entry.totalTime = performanceNow() - start;
return rv;
} else if (fnName === '_mountImageIntoNode' ||
moduleName === 'ReactDOMIDOperations' ||
Expand Down Expand Up @@ -228,16 +248,11 @@ var ReactDefaultPerf = {
return func.apply(this, args);
}

var rootNodeID = fnName === 'mountComponent' ?
args[0] :
this._rootNodeID;
var rootNodeID = getIDOfComposite(this);
var isRender = fnName === '_renderValidatedComponent';
var isMount = fnName === 'mountComponent';

var mountStack = ReactDefaultPerf._mountStack;
var entry = ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
];

if (isRender) {
addValue(entry.counts, rootNodeID, 1);
Expand All @@ -246,10 +261,14 @@ var ReactDefaultPerf = {
mountStack.push(0);
}

ReactDefaultPerf._compositeStack.push(rootNodeID);

start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;

ReactDefaultPerf._compositeStack.pop();

if (isRender) {
addValue(entry.render, rootNodeID, totalTime);
} else if (isMount) {
Expand All @@ -269,6 +288,16 @@ var ReactDefaultPerf = {
};

return rv;
} else if (
(moduleName === 'ReactDOMComponent' ||
moduleName === 'ReactDOMTextComponent') &&
(fnName === 'mountComponent' ||
fnName === 'receiveComponent')) {

rv = func.apply(this, args);
entry.hierarchy[this._rootNodeID] =
ReactDefaultPerf._compositeStack.slice();
return rv;
} else {
return func.apply(this, args);
}
Expand Down
21 changes: 13 additions & 8 deletions src/test/ReactDefaultPerfAnalysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,23 @@ function getUnchangedComponents(measurement) {
// 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 writes = measurement.writes;
var dirtyComposites = {};
Object.keys(writes).forEach(function(id) {
writes[id].forEach(function(write) {
// Root mounting (innerHTML set) is recorded with an ID of ''
if (id !== '') {
measurement.hierarchy[id].forEach((c) => dirtyComposites[c] = true);
}
});
});
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;
}
// See if any of the DOM operations applied to this component's subtree.
if (dirtyComposites[id]) {
isDirty = true;
}
// check if component newly created
if (measurement.created[id]) {
Expand Down

0 comments on commit 663c4b7

Please sign in to comment.