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.
264 lines
8.4 KiB
264 lines
8.4 KiB
/** |
|
* 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 ReactDefaultPerf |
|
* @typechecks static-only |
|
*/ |
|
|
|
'use strict'; |
|
|
|
var DOMProperty = require("./DOMProperty"); |
|
var ReactDefaultPerfAnalysis = require("./ReactDefaultPerfAnalysis"); |
|
var ReactMount = require("./ReactMount"); |
|
var ReactPerf = require("./ReactPerf"); |
|
|
|
var performanceNow = require("./performanceNow"); |
|
|
|
function roundFloat(val) { |
|
return Math.floor(val * 100) / 100; |
|
} |
|
|
|
function addValue(obj, key, val) { |
|
obj[key] = (obj[key] || 0) + val; |
|
} |
|
|
|
var ReactDefaultPerf = { |
|
_allMeasurements: [], // last item in the list is the current one |
|
_mountStack: [0], |
|
_injected: false, |
|
|
|
start: function() { |
|
if (!ReactDefaultPerf._injected) { |
|
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure); |
|
} |
|
|
|
ReactDefaultPerf._allMeasurements.length = 0; |
|
ReactPerf.enableMeasure = true; |
|
}, |
|
|
|
stop: function() { |
|
ReactPerf.enableMeasure = false; |
|
}, |
|
|
|
getLastMeasurements: function() { |
|
return ReactDefaultPerf._allMeasurements; |
|
}, |
|
|
|
printExclusive: function(measurements) { |
|
measurements = measurements || ReactDefaultPerf._allMeasurements; |
|
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements); |
|
console.table(summary.map(function(item) { |
|
return { |
|
'Component class name': item.componentName, |
|
'Total inclusive time (ms)': roundFloat(item.inclusive), |
|
'Exclusive mount time (ms)': roundFloat(item.exclusive), |
|
'Exclusive render time (ms)': roundFloat(item.render), |
|
'Mount time per instance (ms)': roundFloat(item.exclusive / item.count), |
|
'Render time per instance (ms)': roundFloat(item.render / item.count), |
|
'Instances': item.count |
|
}; |
|
})); |
|
// TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct |
|
// number. |
|
}, |
|
|
|
printInclusive: function(measurements) { |
|
measurements = measurements || ReactDefaultPerf._allMeasurements; |
|
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements); |
|
console.table(summary.map(function(item) { |
|
return { |
|
'Owner > component': item.componentName, |
|
'Inclusive time (ms)': roundFloat(item.time), |
|
'Instances': item.count |
|
}; |
|
})); |
|
console.log( |
|
'Total time:', |
|
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms' |
|
); |
|
}, |
|
|
|
getMeasurementsSummaryMap: function(measurements) { |
|
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary( |
|
measurements, |
|
true |
|
); |
|
return summary.map(function(item) { |
|
return { |
|
'Owner > component': item.componentName, |
|
'Wasted time (ms)': item.time, |
|
'Instances': item.count |
|
}; |
|
}); |
|
}, |
|
|
|
printWasted: function(measurements) { |
|
measurements = measurements || ReactDefaultPerf._allMeasurements; |
|
console.table(ReactDefaultPerf.getMeasurementsSummaryMap(measurements)); |
|
console.log( |
|
'Total time:', |
|
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms' |
|
); |
|
}, |
|
|
|
printDOM: function(measurements) { |
|
measurements = measurements || ReactDefaultPerf._allMeasurements; |
|
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements); |
|
console.table(summary.map(function(item) { |
|
var result = {}; |
|
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id; |
|
result['type'] = item.type; |
|
result['args'] = JSON.stringify(item.args); |
|
return result; |
|
})); |
|
console.log( |
|
'Total time:', |
|
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms' |
|
); |
|
}, |
|
|
|
_recordWrite: function(id, fnName, totalTime, args) { |
|
// TODO: totalTime isn't that useful since it doesn't count paints/reflows |
|
var writes = |
|
ReactDefaultPerf |
|
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1] |
|
.writes; |
|
writes[id] = writes[id] || []; |
|
writes[id].push({ |
|
type: fnName, |
|
time: totalTime, |
|
args: args |
|
}); |
|
}, |
|
|
|
measure: function(moduleName, fnName, func) { |
|
return function() {for (var args=[],$__0=0,$__1=arguments.length;$__0<$__1;$__0++) args.push(arguments[$__0]); |
|
var totalTime; |
|
var rv; |
|
var start; |
|
|
|
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({ |
|
exclusive: {}, |
|
inclusive: {}, |
|
render: {}, |
|
counts: {}, |
|
writes: {}, |
|
displayNames: {}, |
|
totalTime: 0 |
|
}); |
|
start = performanceNow(); |
|
rv = func.apply(this, args); |
|
ReactDefaultPerf._allMeasurements[ |
|
ReactDefaultPerf._allMeasurements.length - 1 |
|
].totalTime = performanceNow() - start; |
|
return rv; |
|
} else if (fnName === '_mountImageIntoNode' || |
|
moduleName === 'ReactDOMIDOperations') { |
|
start = performanceNow(); |
|
rv = func.apply(this, args); |
|
totalTime = performanceNow() - start; |
|
|
|
if (fnName === '_mountImageIntoNode') { |
|
var mountID = ReactMount.getID(args[1]); |
|
ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]); |
|
} else if (fnName === 'dangerouslyProcessChildrenUpdates') { |
|
// special format |
|
args[0].forEach(function(update) { |
|
var writeArgs = {}; |
|
if (update.fromIndex !== null) { |
|
writeArgs.fromIndex = update.fromIndex; |
|
} |
|
if (update.toIndex !== null) { |
|
writeArgs.toIndex = update.toIndex; |
|
} |
|
if (update.textContent !== null) { |
|
writeArgs.textContent = update.textContent; |
|
} |
|
if (update.markupIndex !== null) { |
|
writeArgs.markup = args[1][update.markupIndex]; |
|
} |
|
ReactDefaultPerf._recordWrite( |
|
update.parentID, |
|
update.type, |
|
totalTime, |
|
writeArgs |
|
); |
|
}); |
|
} else { |
|
// basic format |
|
ReactDefaultPerf._recordWrite( |
|
args[0], |
|
fnName, |
|
totalTime, |
|
Array.prototype.slice.call(args, 1) |
|
); |
|
} |
|
return rv; |
|
} else if (moduleName === 'ReactCompositeComponent' && ( |
|
(// TODO: receiveComponent()? |
|
(fnName === 'mountComponent' || |
|
fnName === 'updateComponent' || fnName === '_renderValidatedComponent')))) { |
|
|
|
if (typeof this._currentElement.type === 'string') { |
|
return func.apply(this, args); |
|
} |
|
|
|
var rootNodeID = fnName === 'mountComponent' ? |
|
args[0] : |
|
this._rootNodeID; |
|
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); |
|
} else if (isMount) { |
|
mountStack.push(0); |
|
} |
|
|
|
start = performanceNow(); |
|
rv = func.apply(this, args); |
|
totalTime = performanceNow() - start; |
|
|
|
if (isRender) { |
|
addValue(entry.render, rootNodeID, totalTime); |
|
} else if (isMount) { |
|
var subMountTime = mountStack.pop(); |
|
mountStack[mountStack.length - 1] += totalTime; |
|
addValue(entry.exclusive, rootNodeID, totalTime - subMountTime); |
|
addValue(entry.inclusive, rootNodeID, totalTime); |
|
} else { |
|
addValue(entry.inclusive, rootNodeID, totalTime); |
|
} |
|
|
|
entry.displayNames[rootNodeID] = { |
|
current: this.getName(), |
|
owner: this._currentElement._owner ? |
|
this._currentElement._owner.getName() : |
|
'<root>' |
|
}; |
|
|
|
return rv; |
|
} else { |
|
return func.apply(this, args); |
|
} |
|
}; |
|
} |
|
}; |
|
|
|
module.exports = ReactDefaultPerf;
|
|
|