Skip to content

Commit

Permalink
Merge pull request #6647 from gaearon/bye-bye-reactperf
Browse files Browse the repository at this point in the history
Replace ReactPerf with new implementation
  • Loading branch information
gaearon committed May 5, 2016
2 parents fbe9002 + 82e363c commit b6a6078
Show file tree
Hide file tree
Showing 26 changed files with 165 additions and 933 deletions.
2 changes: 1 addition & 1 deletion grunt/tasks/npm-react-addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var addons = {
docs: 'two-way-binding-helpers',
},
Perf: {
module: 'ReactPerfAnalysis',
module: 'ReactPerf',
name: 'perf',
docs: 'perf',
},
Expand Down
2 changes: 1 addition & 1 deletion src/addons/ReactWithAddons.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ React.addons = {
};

if (__DEV__) {
React.addons.Perf = require('ReactPerfAnalysis');
React.addons.Perf = require('ReactPerf');
React.addons.TestUtils = require('ReactTestUtils');
}

Expand Down
7 changes: 4 additions & 3 deletions src/isomorphic/ReactDebugTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function resetMeasurements() {
tree[id] = {
displayName: ReactComponentTreeDevtool.getDisplayName(id),
text: ReactComponentTreeDevtool.getText(id),
updateCount: ReactComponentTreeDevtool.getUpdateCount(id),
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
// Text nodes don't have owners but this is close enough.
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
Expand Down Expand Up @@ -144,7 +145,7 @@ var ReactDebugTool = {
onBeginLifeCycleTimer(debugID, timerType) {
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
if (__DEV__) {
if (isProfiling) {
if (isProfiling && currentFlushNesting > 0) {
warning(
!currentTimerType,
'There is an internal error in the React performance measurement code. ' +
Expand All @@ -162,7 +163,7 @@ var ReactDebugTool = {
},
onEndLifeCycleTimer(debugID, timerType) {
if (__DEV__) {
if (isProfiling) {
if (isProfiling && currentFlushNesting > 0) {
warning(
currentTimerType === timerType,
'There is an internal error in the React performance measurement code. ' +
Expand All @@ -175,7 +176,7 @@ var ReactDebugTool = {
currentFlushMeasurements.push({
timerType,
instanceID: debugID,
duration: performance.now() - currentTimerStartTime,
duration: performanceNow() - currentTimerStartTime,
});
currentTimerStartTime = null;
currentTimerDebugID = null;
Expand Down
90 changes: 50 additions & 40 deletions src/isomorphic/ReactPerfAnalysis.js → src/isomorphic/ReactPerf.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* 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 ReactPerfAnalysis
* @providesModule ReactPerf
*/

'use strict';
Expand All @@ -27,12 +27,10 @@ function getExclusive(flushHistory = getFlushHistory()) {
var aggregatedStats = {};
var affectedIDs = {};

function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) {
var {displayName} = treeSnapshot[instanceID];

var key = displayName;
var stats = aggregatedStats[key];

if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
Expand All @@ -43,7 +41,12 @@ function getExclusive(flushHistory = getFlushHistory()) {
totalDuration: 0,
};
}

if (!stats.durations[timerType]) {
stats.durations[timerType] = 0;
}
if (!stats.counts[timerType]) {
stats.counts[timerType] = 0;
}
affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}
Expand All @@ -52,17 +55,9 @@ function getExclusive(flushHistory = getFlushHistory()) {
var {measurements, treeSnapshot} = flush;
measurements.forEach(measurement => {
var {duration, instanceID, timerType} = measurement;
updateAggregatedStats(treeSnapshot, instanceID, stats => {
updateAggregatedStats(treeSnapshot, instanceID, timerType, stats => {
stats.totalDuration += duration;

if (!stats.durations[timerType]) {
stats.durations[timerType] = 0;
}
stats.durations[timerType] += duration;

if (!stats.counts[timerType]) {
stats.counts[timerType] = 0;
}
stats.counts[timerType]++;
});
});
Expand All @@ -73,20 +68,20 @@ function getExclusive(flushHistory = getFlushHistory()) {
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.totalDuration - a.totalDuration);
.sort((a, b) =>
b.totalDuration - a.totalDuration
);
}

function getInclusive(flushHistory = getFlushHistory(), wastedOnly) {
function getInclusive(flushHistory = getFlushHistory()) {
var aggregatedStats = {};
var affectedIDs = {};

function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];

var owner = treeSnapshot[ownerID];
var key = `${owner ? owner.displayName : '(no owner)'} > ${displayName}`;
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
var stats = aggregatedStats[key];

if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
Expand All @@ -96,20 +91,19 @@ function getInclusive(flushHistory = getFlushHistory(), wastedOnly) {
renderCount: 0,
};
}

affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}

var hasRenderedByID = {};
var isCompositeByID = {};
flushHistory.forEach(flush => {
var {measurements} = flush;
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
if (timerType !== 'render') {
return;
}
hasRenderedByID[instanceID] = true;
isCompositeByID[instanceID] = true;
});
});

Expand All @@ -125,7 +119,9 @@ function getInclusive(flushHistory = getFlushHistory(), wastedOnly) {
});
var nextParentID = instanceID;
while (nextParentID) {
if (hasRenderedByID[nextParentID]) {
// As we traverse parents, only count inclusive time towards composites.
// We know something is a composite if its render() was called.
if (isCompositeByID[nextParentID]) {
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
Expand All @@ -140,7 +136,9 @@ function getInclusive(flushHistory = getFlushHistory(), wastedOnly) {
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
.sort((a, b) =>
b.inclusiveRenderDuration - a.inclusiveRenderDuration
);
}

function getWasted(flushHistory = getFlushHistory()) {
Expand All @@ -149,11 +147,9 @@ function getWasted(flushHistory = getFlushHistory()) {

function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
var {displayName, ownerID} = treeSnapshot[instanceID];

var owner = treeSnapshot[ownerID];
var key = `${owner ? owner.displayName : '(no owner)'} > ${displayName}`;
var key = (owner ? owner.displayName + ' > ' : '') + displayName;
var stats = aggregatedStats[key];

if (!stats) {
affectedIDs[key] = {};
stats = aggregatedStats[key] = {
Expand All @@ -163,25 +159,27 @@ function getWasted(flushHistory = getFlushHistory()) {
renderCount: 0,
};
}

affectedIDs[key][instanceID] = true;
applyUpdate(stats);
}

flushHistory.forEach(flush => {
var {measurements, treeSnapshot, operations} = flush;
var dirtyInstanceIDs = {};
var isDefinitelyNotWastedByID = {};

// Find native components associated with an operation in this batch.
// Mark all components in their parent tree as definitely not wasted.
operations.forEach(operation => {
var {instanceID} = operation;

var nextParentID = instanceID;
while (nextParentID) {
dirtyInstanceIDs[nextParentID] = true;
isDefinitelyNotWastedByID[nextParentID] = true;
nextParentID = treeSnapshot[nextParentID].parentID;
}
});

// Find composite components that rendered in this batch.
// These are potential candidates for being wasted renders.
var renderedCompositeIDs = {};
measurements.forEach(measurement => {
var {instanceID, timerType} = measurement;
Expand All @@ -196,21 +194,31 @@ function getWasted(flushHistory = getFlushHistory()) {
if (timerType !== 'render') {
return;
}

// If there was a DOM update below this component, or it has just been
// mounted, its render() is not considered wasted.
var { updateCount } = treeSnapshot[instanceID];
if (dirtyInstanceIDs[instanceID] || updateCount === 0) {
if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) {
return;
}

// We consider this render() wasted.
updateAggregatedStats(treeSnapshot, instanceID, stats => {
stats.renderCount++;
});

var nextParentID = instanceID;
while (nextParentID) {
if (!renderedCompositeIDs[nextParentID]) {
break;
// Any parents rendered during this batch are considered wasted
// unless we previously marked them as dirty.
var isWasted =
renderedCompositeIDs[nextParentID] &&
!isDefinitelyNotWastedByID[nextParentID];
if (isWasted) {
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
}
updateAggregatedStats(treeSnapshot, nextParentID, stats => {
stats.inclusiveRenderDuration += duration;
});
nextParentID = treeSnapshot[nextParentID].parentID;
}
});
Expand All @@ -221,7 +229,9 @@ function getWasted(flushHistory = getFlushHistory()) {
...aggregatedStats[key],
instanceCount: Object.keys(affectedIDs[key]).length,
}))
.sort((a, b) => b.inclusiveRenderDuration - a.inclusiveRenderDuration);
.sort((a, b) =>
b.inclusiveRenderDuration - a.inclusiveRenderDuration
);
}

function getOperations(flushHistory = getFlushHistory()) {
Expand All @@ -232,7 +242,7 @@ function getOperations(flushHistory = getFlushHistory()) {
var {instanceID, type, payload} = operation;
var {displayName, ownerID} = treeSnapshot[instanceID];
var owner = treeSnapshot[ownerID];
var key = `${(owner ? owner.displayName : '(no owner)')} > ${displayName}`;
var key = (owner ? owner.displayName + ' > ' : '') + displayName;

stats.push({
flushIndex,
Expand Down Expand Up @@ -342,7 +352,7 @@ function stop() {
}

var ReactPerfAnalysis = {
getFlushHistory,
getLastMeasurements: getFlushHistory,
getExclusive,
getInclusive,
getWasted,
Expand Down
Loading

0 comments on commit b6a6078

Please sign in to comment.