@samitouri / QOS-React-2 / commits / 663c4b7d2e

Stop relying on hierarchical IDs in ReactDefaultPerf

Ben Alpert committed Oct 18, 2015 at 15:25 UTC 663c4b7d2ecbb93944da7e127401b782a663b470
4 files changed +67 -23
src/renderers/dom/shared/ReactDOMComponent.js
+2 -2
@@ -1180,9 +1180,9 @@ ReactDOMComponent.Mixin = {
1180
1181 };
1182
1183 -ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
1183 +ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
1184 mountComponent: 'mountComponent',
1185 - updateComponent: 'updateComponent',
1185 + receiveComponent: 'receiveComponent',
1186 });
1187
1188 assign(
src/renderers/dom/shared/ReactDOMTextComponent.js
+10
@@ -18,6 +18,7 @@ var DOMPropertyOperations = require('DOMPropertyOperations');
18 var ReactComponentBrowserEnvironment =
19 require('ReactComponentBrowserEnvironment');
20 var ReactDOMComponentTree = require('ReactDOMComponentTree');
21 +var ReactPerf = require('ReactPerf');
22
23 var assign = require('Object.assign');
24 var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
@@ -153,4 +154,13 @@ assign(ReactDOMTextComponent.prototype, {
154
155 });
156
157 +ReactPerf.measureMethods(
158 + ReactDOMTextComponent.prototype,
159 + 'ReactDOMTextComponent',
160 + {
161 + mountComponent: 'mountComponent',
162 + receiveComponent: 'receiveComponent',
163 + }
164 +);
165 +
166 module.exports = ReactDOMTextComponent;
src/test/ReactDefaultPerf.js
+42 -13
@@ -28,9 +28,26 @@ function addValue(obj, key, val) {
28 obj[key] = (obj[key] || 0) + val;
29 }
30
31 +// Composites don't have any built-in ID: we have to make our own
32 +var compositeIDMap;
33 +var compositeIDCounter = 17000;
34 +function getIDOfComposite(inst) {
35 + if (!compositeIDMap) {
36 + compositeIDMap = new WeakMap();
37 + }
38 + if (compositeIDMap.has(inst)) {
39 + return compositeIDMap.get(inst);
40 + } else {
41 + var id = compositeIDCounter++;
42 + compositeIDMap.set(inst, id);
43 + return id;
44 + }
45 +}
46 +
47 var ReactDefaultPerf = {
48 _allMeasurements: [], // last item in the list is the current one
49 _mountStack: [0],
50 + _compositeStack: [],
51 _injected: false,
52
53 start: function() {
@@ -125,10 +142,10 @@ var ReactDefaultPerf = {
142
143 _recordWrite: function(id, fnName, totalTime, args) {
144 // TODO: totalTime isn't that useful since it doesn't count paints/reflows
128 - var writes =
145 + var entry =
146 ReactDefaultPerf
130 - ._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]
131 - .writes;
147 + ._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
148 + var writes = entry.writes;
149 writes[id] = writes[id] || [];
150 writes[id].push({
151 type: fnName,
@@ -143,27 +160,30 @@ var ReactDefaultPerf = {
160 var rv;
161 var start;
162
163 + var entry = ReactDefaultPerf._allMeasurements[
164 + ReactDefaultPerf._allMeasurements.length - 1
165 + ];
166 +
167 if (fnName === '_renderNewRootComponent' ||
168 fnName === 'flushBatchedUpdates') {
169 // A "measurement" is a set of metrics recorded for each flush. We want
170 // to group the metrics for a given flush together so we can look at the
171 // components that rendered and the DOM operations that actually
172 // happened to determine the amount of "wasted work" performed.
152 - ReactDefaultPerf._allMeasurements.push({
173 + ReactDefaultPerf._allMeasurements.push(entry = {
174 exclusive: {},
175 inclusive: {},
176 render: {},
177 counts: {},
178 writes: {},
179 displayNames: {},
180 + hierarchy: {},
181 totalTime: 0,
182 created: {},
183 });
184 start = performanceNow();
185 rv = func.apply(this, args);
164 - ReactDefaultPerf._allMeasurements[
165 - ReactDefaultPerf._allMeasurements.length - 1
166 - ].totalTime = performanceNow() - start;
186 + entry.totalTime = performanceNow() - start;
187 return rv;
188 } else if (fnName === '_mountImageIntoNode' ||
189 moduleName === 'ReactDOMIDOperations' ||
@@ -228,16 +248,11 @@ var ReactDefaultPerf = {
248 return func.apply(this, args);
249 }
250
231 - var rootNodeID = fnName === 'mountComponent' ?
232 - args[0] :
233 - this._rootNodeID;
251 + var rootNodeID = getIDOfComposite(this);
252 var isRender = fnName === '_renderValidatedComponent';
253 var isMount = fnName === 'mountComponent';
254
255 var mountStack = ReactDefaultPerf._mountStack;
238 - var entry = ReactDefaultPerf._allMeasurements[
239 - ReactDefaultPerf._allMeasurements.length - 1
240 - ];
256
257 if (isRender) {
258 addValue(entry.counts, rootNodeID, 1);
@@ -246,10 +261,14 @@ var ReactDefaultPerf = {
261 mountStack.push(0);
262 }
263
264 + ReactDefaultPerf._compositeStack.push(rootNodeID);
265 +
266 start = performanceNow();
267 rv = func.apply(this, args);
268 totalTime = performanceNow() - start;
269
270 + ReactDefaultPerf._compositeStack.pop();
271 +
272 if (isRender) {
273 addValue(entry.render, rootNodeID, totalTime);
274 } else if (isMount) {
@@ -269,6 +288,16 @@ var ReactDefaultPerf = {
288 };
289
290 return rv;
291 + } else if (
292 + (moduleName === 'ReactDOMComponent' ||
293 + moduleName === 'ReactDOMTextComponent') &&
294 + (fnName === 'mountComponent' ||
295 + fnName === 'receiveComponent')) {
296 +
297 + rv = func.apply(this, args);
298 + entry.hierarchy[this._rootNodeID] =
299 + ReactDefaultPerf._compositeStack.slice();
300 + return rv;
301 } else {
302 return func.apply(this, args);
303 }
src/test/ReactDefaultPerfAnalysis.js
+13 -8
@@ -173,18 +173,23 @@ function getUnchangedComponents(measurement) {
173 // render anything to the DOM and return a mapping of their ID to
174 // the amount of time it took to render the entire subtree.
175 var cleanComponents = {};
176 - var dirtyLeafIDs = Object.keys(measurement.writes);
176 + var writes = measurement.writes;
177 + var dirtyComposites = {};
178 + Object.keys(writes).forEach(function(id) {
179 + writes[id].forEach(function(write) {
180 + // Root mounting (innerHTML set) is recorded with an ID of ''
181 + if (id !== '') {
182 + measurement.hierarchy[id].forEach((c) => dirtyComposites[c] = true);
183 + }
184 + });
185 + });
186 var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
187
188 for (var id in allIDs) {
189 var isDirty = false;
181 - // For each component that rendered, see if a component that triggered
182 - // a DOM op is in its subtree.
183 - for (var i = 0; i < dirtyLeafIDs.length; i++) {
184 - if (dirtyLeafIDs[i].indexOf(id) === 0) {
185 - isDirty = true;
186 - break;
187 - }
190 + // See if any of the DOM operations applied to this component's subtree.
191 + if (dirtyComposites[id]) {
192 + isDirty = true;
193 }
194 // check if component newly created
195 if (measurement.created[id]) {