Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81152 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule ReactDefaultPerfAnalysis
10
*/
11
12
var assign = require('Object.assign');
13
14
// Don't try to save users less than 1.2ms (a number I made up)
15
var DONT_CARE_THRESHOLD = 1.2;
16
var DOM_OPERATION_TYPES = {
17
'mountImageIntoNode': 'set innerHTML',
18
INSERT_MARKUP: 'set innerHTML',
19
MOVE_EXISTING: 'move',
20
REMOVE_NODE: 'remove',
21
TEXT_CONTENT: 'set textContent',
22
'updatePropertyByID': 'update attribute',
23
'deletePropertyByID': 'delete attribute',
24
'updateStylesByID': 'update styles',
25
'updateInnerHTMLByID': 'set innerHTML',
26
'dangerouslyReplaceNodeWithMarkupByID': 'replace'
27
};
28
29
function getTotalTime(measurements) {
30
// TODO: return number of DOM ops? could be misleading.
31
// TODO: measure dropped frames after reconcile?
32
// TODO: log total time of each reconcile and the top-level component
33
// class that triggered it.
34
var totalTime = 0;
35
for (var i = 0; i < measurements.length; i++) {
36
var measurement = measurements[i];
37
totalTime += measurement.totalTime;
38
}
39
return totalTime;
40
}
41
42
function getDOMSummary(measurements) {
43
var items = [];
44
for (var i = 0; i < measurements.length; i++) {
45
var measurement = measurements[i];
46
var id;
47
48
for (id in measurement.writes) {
49
measurement.writes[id].forEach(function(write) {
50
items.push({
51
id: id,
52
type: DOM_OPERATION_TYPES[write.type] || write.type,
53
args: write.args
54
});
55
});
56
}
57
}
58
return items;
59
}
60
61
function getExclusiveSummary(measurements) {
62
var candidates = {};
63
var displayName;
64
65
for (var i = 0; i < measurements.length; i++) {
66
var measurement = measurements[i];
67
var allIDs = assign(
68
{},
69
measurement.exclusive,
70
measurement.inclusive
71
);
72
73
for (var id in allIDs) {
74
displayName = measurement.displayNames[id].current;
75
76
candidates[displayName] = candidates[displayName] || {
77
componentName: displayName,
78
inclusive: 0,
79
exclusive: 0,
80
render: 0,
81
count: 0
82
};
83
if (measurement.render[id]) {
84
candidates[displayName].render += measurement.render[id];
85
}
86
if (measurement.exclusive[id]) {
87
candidates[displayName].exclusive += measurement.exclusive[id];
88
}
89
if (measurement.inclusive[id]) {
90
candidates[displayName].inclusive += measurement.inclusive[id];
91
}
92
if (measurement.counts[id]) {
93
candidates[displayName].count += measurement.counts[id];
94
}
95
}
96
}
97
98
// Now make a sorted array with the results.
99
var arr = [];
100
for (displayName in candidates) {
101
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
102
arr.push(candidates[displayName]);
103
}
104
}
105
106
arr.sort(function(a, b) {
107
return b.exclusive - a.exclusive;
108
});
109
110
return arr;
111
}
112
113
function getInclusiveSummary(measurements, onlyClean) {
114
var candidates = {};
115
var inclusiveKey;
116
117
for (var i = 0; i < measurements.length; i++) {
118
var measurement = measurements[i];
119
var allIDs = assign(
120
{},
121
measurement.exclusive,
122
measurement.inclusive
123
);
124
var cleanComponents;
125
126
if (onlyClean) {
127
cleanComponents = getUnchangedComponents(measurement);
128
}
129
130
for (var id in allIDs) {
131
if (onlyClean && !cleanComponents[id]) {
132
continue;
133
}
134
135
var displayName = measurement.displayNames[id];
136
137
// Inclusive time is not useful for many components without knowing where
138
// they are instantiated. So we aggregate inclusive time with both the
139
// owner and current displayName as the key.
140
inclusiveKey = displayName.owner + ' > ' + displayName.current;
141
142
candidates[inclusiveKey] = candidates[inclusiveKey] || {
143
componentName: inclusiveKey,
144
time: 0,
145
count: 0
146
};
147
148
if (measurement.inclusive[id]) {
149
candidates[inclusiveKey].time += measurement.inclusive[id];
150
}
151
if (measurement.counts[id]) {
152
candidates[inclusiveKey].count += measurement.counts[id];
153
}
154
}
155
}
156
157
// Now make a sorted array with the results.
158
var arr = [];
159
for (inclusiveKey in candidates) {
160
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
161
arr.push(candidates[inclusiveKey]);
162
}
163
}
164
165
arr.sort(function(a, b) {
166
return b.time - a.time;
167
});
168
169
return arr;
170
}
171
172
function getUnchangedComponents(measurement) {
173
// For a given reconcile, look at which components did not actually
174
// render anything to the DOM and return a mapping of their ID to
175
// the amount of time it took to render the entire subtree.
176
var cleanComponents = {};
177
var dirtyLeafIDs = Object.keys(measurement.writes);
178
var allIDs = assign({}, measurement.exclusive, measurement.inclusive);
179
180
for (var id in allIDs) {
181
var isDirty = false;
182
// For each component that rendered, see if a component that triggered
183
// a DOM op is in its subtree.
184
for (var i = 0; i < dirtyLeafIDs.length; i++) {
185
if (dirtyLeafIDs[i].indexOf(id) === 0) {
186
isDirty = true;
187
break;
188
}
189
}
190
if (!isDirty && measurement.counts[id] > 0) {
191
cleanComponents[id] = true;
192
}
193
}
194
return cleanComponents;
195
}
196
197
var ReactDefaultPerfAnalysis = {
198
getExclusiveSummary: getExclusiveSummary,
199
getInclusiveSummary: getInclusiveSummary,
200
getDOMSummary: getDOMSummary,
201
getTotalTime: getTotalTime
202
};
203
204
module.exports = ReactDefaultPerfAnalysis;
205
206