Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new ReactPerf #6046

Merged
merged 1 commit into from
Apr 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: 'ReactDefaultPerf',
module: 'ReactPerfAnalysis',
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('ReactDefaultPerf');
React.addons.Perf = require('ReactPerfAnalysis');
React.addons.TestUtils = require('ReactTestUtils');
}

Expand Down
156 changes: 154 additions & 2 deletions src/isomorphic/ReactDebugTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

'use strict';

var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ExecutionEnvironment = require('ExecutionEnvironment');

var performanceNow = require('performanceNow');
var warning = require('warning');

var eventHandlers = [];
Expand All @@ -37,6 +39,56 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
}
}

var isProfiling = false;
var flushHistory = [];
var currentFlushNesting = 0;
var currentFlushMeasurements = null;
var currentFlushStartTime = null;
var currentTimerDebugID = null;
var currentTimerStartTime = null;
var currentTimerType = null;

function resetMeasurements() {
if (__DEV__) {
if (!isProfiling || currentFlushNesting === 0) {
currentFlushStartTime = null;
currentFlushMeasurements = null;
return;
}

var previousStartTime = currentFlushStartTime;
var previousMeasurements = currentFlushMeasurements || [];
var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();

if (previousMeasurements.length || previousOperations.length) {
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
flushHistory.push({
duration: performanceNow() - previousStartTime,
measurements: previousMeasurements || [],
operations: previousOperations || [],
treeSnapshot: registeredIDs.reduce((tree, id) => {
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
var parentID = ReactComponentTreeDevtool.getParentID(id);
tree[id] = {
displayName: ReactComponentTreeDevtool.getDisplayName(id),
text: ReactComponentTreeDevtool.getText(id),
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
// Text nodes don't have owners but this is close enough.
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
parentID,
};
return tree;
}, {}),
});
}

currentFlushStartTime = performanceNow();
currentFlushMeasurements = [];
ReactComponentTreeDevtool.purgeUnmountedComponents();
ReactNativeOperationHistoryDevtool.clearHistory();
}
}

var ReactDebugTool = {
addDevtool(devtool) {
eventHandlers.push(devtool);
Expand All @@ -49,6 +101,95 @@ var ReactDebugTool = {
}
}
},
beginProfiling() {
if (__DEV__) {
if (isProfiling) {
return;
}

isProfiling = true;
flushHistory.length = 0;
resetMeasurements();
}
},
endProfiling() {
if (__DEV__) {
if (!isProfiling) {
return;
}

isProfiling = false;
resetMeasurements();
}
},
getFlushHistory() {
if (__DEV__) {
return flushHistory;
}
},
onBeginFlush() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain a bit on why the flush batch is important? Does it matter if the implementation batches or just does the work whenever?

Copy link
Collaborator Author

@gaearon gaearon Apr 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comes down to the way printWasted() heuristic works. It says “count a render as wasted if no DOM operations below were made during the same batch”. I was trying to replicate the existing behavior so this is the main reason.

I think we may want to reconsider this as part of #6632. I’d leave it as is for now though.

if (__DEV__) {
currentFlushNesting++;
resetMeasurements();
}
emitEvent('onBeginFlush');
},
onEndFlush() {
if (__DEV__) {
resetMeasurements();
currentFlushNesting--;
}
emitEvent('onEndFlush');
},
onBeginLifeCycleTimer(debugID, timerType) {
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
if (__DEV__) {
if (isProfiling) {
warning(
!currentTimerType,
'There is an internal error in the React performance measurement code. ' +
'Did not expect %s timer to start while %s timer is still in ' +
'progress for %s instance.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentTimerStartTime = performanceNow();
currentTimerDebugID = debugID;
currentTimerType = timerType;
}
}
},
onEndLifeCycleTimer(debugID, timerType) {
if (__DEV__) {
if (isProfiling) {
warning(
currentTimerType === timerType,
'There is an internal error in the React performance measurement code. ' +
'We did not expect %s timer to stop while %s timer is still in ' +
'progress for %s instance. Please report this as a bug in React.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentFlushMeasurements.push({
timerType,
instanceID: debugID,
duration: performance.now() - currentTimerStartTime,
});
currentTimerStartTime = null;
currentTimerDebugID = null;
currentTimerType = null;
}
}
emitEvent('onEndLifeCycleTimer', debugID, timerType);
},
onBeginReconcilerTimer(debugID, timerType) {
emitEvent('onBeginReconcilerTimer', debugID, timerType);
},
onEndReconcilerTimer(debugID, timerType) {
emitEvent('onEndReconcilerTimer', debugID, timerType);
},
onBeginProcessingChildContext() {
emitEvent('onBeginProcessingChildContext');
},
Expand Down Expand Up @@ -90,6 +231,17 @@ var ReactDebugTool = {
},
};

ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
if (__DEV__) {
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
ReactDebugTool.beginProfiling();
}
}

module.exports = ReactDebugTool;
Loading