@samitouri / QOS-React / commits / 181e8aff30

Added planned Profiler architecture to OVERVIEW doc to share with others

Brian Vaughn committed Mar 6, 2019 at 18:12 UTC 181e8aff3004be09d66ca86bb486eb0fad031181
1 file changed +217 -6
OVERVIEW.md
+217 -6
@@ -8,7 +8,9 @@ One of the largest performance bottlenecks of the old React DevTools was the amo
8
9 The old DevTools also rendered the entire application tree in the form of a large DOM structure of nested nodes. A secondary goal of the rewrite was to avoid rendering unnecessary nodes by using a windowing library (specifically [react-window](https://github.com/bvaughn/react-window)).
10
11 -## Serializing the tree
11 +## Elements panel
12 +
13 +### Serializing the tree
14
15 Every React commit that changes the tree in a way DevTools cares about results in an "_operations_" message being sent across the bridge. These messages are lightweight patches that describe the changes that were made. (We don't resend the full tree structure like in legacy DevTools.)
16
@@ -16,7 +18,7 @@ The payload for each message is a typed array. The first entry is a number ident
18
19 We only send the following bits of information: element type, id, parent id, owner id, name, and key. Additional information (e.g. props, state) requires a separate "_inspectElement_" message.
20
19 -### Adding a root node
21 +#### Adding a root node
22
23 Adding a root to the tree requires sending 3 numbers:
24
@@ -33,7 +35,7 @@ For example, adding a root fiber with an id of 1:
35 ]
36 ```
37
36 -### Adding a leaf node
38 +#### Adding a leaf node
39
40 Adding a leaf node takes a variable number of numbers since we need to decode the name (and potentially the key):
41
@@ -63,7 +65,7 @@ For example, adding a function component `<Foo>` with an id 2:
65 ]
66 ```
67
66 -### Removing a node
68 +#### Removing a node
69
70 Removing a fiber from the tree (a root or a leaf) only requires sending 2 numbers:
71
@@ -78,7 +80,7 @@ For example, removing a root fiber with an id of 1:
80 ]
81 ```
82
81 -### Re-ordering children
83 +#### Re-ordering children
84
85 1. re-order children constant (`3`)
86 1. fiber id
@@ -114,7 +116,7 @@ The frontend stores its information about the tree in a map of id to objects wit
116
117 <sup>2</sup> The `weight` of an element is the number of elements (including itself) below it in the tree. We cache this property so that we can quickly determine the total number of Elements as well as to find the Nth element within that set. (This enables us to use windowing.) This value needs to be adjusted each time elements are added or removed from the tree, but we amortize this over time to avoid any big performance hits when rendering the tree.
118
117 -### Finding the element at index N
119 +#### Finding the element at index N
120
121 The tree data structure lets us impose an order on elements and "quickly" find the Nth one using the `weight` attribute.
122
@@ -160,3 +162,212 @@ while (index !== currentWeight) {
162 }
163 }
164 ```
165 +
166 +## Profiler
167 +
168 +All profiling information is stored on the backend while profiling is in progress. (Avoiding sending traffic across the bridge reduces the performance overhead of running the profiler.)
169 +
170 +### Profiling summary
171 +
172 +Upon completion, a "_profileSummary_" message is sent across the bridge. This message is a typed array summarizing the profiling session. It contains a repeating sequence of values (per root):
173 +
174 +1. root id
175 +1. number of interactions for the root in this profiling session
176 +1. number of commits for the root in this profiling session
177 +
178 +Followed by a series of tuples for each commit:
179 +
180 +1. timestamp (relative to when profiling was started)
181 +1. duration of commit
182 +
183 +This is the minimal information required to render the main Profiler interface.
184 +
185 +For example, an application with two React roots might send a summary like this:
186 +```js
187 +[
188 + 1, // root id
189 + 0, // no interactions were logged during this session
190 + 3, // number of commits
191 + 210, // first commit started 210ms after profiling began
192 + 10, // and took 10ms
193 + 284, // second commit started 284ms after profiling began
194 + 13, // and took 13ms
195 + 303, // third commit started 303ms after profiling began
196 + 5, // and took 5ms
197 +
198 + 63, // root id
199 + 1, // one interaction was logged during this session
200 + 2, // number of commits
201 + 513, // first commit started 513ms after profiling began
202 + 8, // first commit took 8ms
203 + 711, // second commit started 711ms after profiling began
204 + 22, // second commit took 22ms
205 +]
206 +```
207 +
208 +Additional information (e.g. which components were part of a specific commit, which interactions were logged) is lazily requested by the frontend as a user interacts with the profiling data.
209 +
210 +### Commit details
211 +
212 +When a particular commit is selected, the frontend polls the backend for the information necessary to display the ["flame chart"](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#flame-chart) and ["ranked chart"](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#ranked-chart) views. This information includes the time and duration of the commit, any interactions that were part of the commit, and a tree representing the state of the React application as of that commit.
213 +
214 +The frontend sends a "_profileCommitDetails_" message specifying which root and commit (index) it is interested in. The backend sends a response to fill in missing details about the commit.
215 +
216 +The response always beginning with 3 values:
217 +
218 +1. root id
219 +1. commit index (which commit this describes)
220 +1. number of interactions
221 +
222 +Next is a series of interactions (depending on the number specified previously) consisting of:
223 +
224 +1. interaction id
225 +1. timestamp (when the interaction was traced relative to when profiling started)
226 +1. UTF encoded interaction display name size
227 + * (followed by this number of encoded values)
228 +
229 +Finally a flattened representation of the React tree as of this commit operation:
230 +
231 +1. element id
232 +1. parent id
233 +1. base duration
234 +1. self duration
235 +1. actual duration
236 +1. UTF encoded display name size
237 + * (followed by this number of encoded values)
238 +
239 +Here is an example commit containing two interactions and a tree of three React components:
240 +
241 +```js
242 +[
243 + 1, // root id
244 + 0, // commit index (the first commit)
245 + 2, // the number of interactions (represented below)
246 +
247 + 1, // first interaction id
248 + 4, // time when interaction was first traced
249 + 3, // encoded interaction name size
250 + 70, // "F"
251 + 111, // "o"
252 + 111, // "o"
253 +
254 + 1, // second interaction id
255 + 5, // time when interaction was first traced
256 + 3, // encoded interaction name size
257 + 66, // "B"
258 + 97, // "a"
259 + 114, // "r"
260 +
261 + 1, // root fiber id
262 + -1, // parent id (signifies the fiber is a root)
263 + 15, // base duration
264 + 4, // self duration
265 + 15, // actual duration
266 + 4, // UTF encoded display name size
267 + 76, // "L"
268 + 105, // "i"
269 + 115, // "s"
270 + 116, // "t"
271 +
272 + 2, // fiber id
273 + 1, // parent id
274 + 11, // base duration
275 + 8, // self duration
276 + 11, // actual duration
277 + 4, // UTF encoded display name size
278 + 73, // "I"
279 + 116, // "t"
280 + 101, // "e"
281 + 109, // "m"
282 +
283 + 2, // fiber id
284 + 1, // parent id
285 + 8, // base duration
286 + 0, // self duration
287 + 0, // actual duration (this component didn't render during this commit)
288 + 4, // UTF encoded display name size
289 + 73, // "I"
290 + 116, // "t"
291 + 101, // "e"
292 + 109, // "m"
293 +]
294 +```
295 +
296 +### Component commits
297 +
298 +When a particular component (fiber) is selected, the frontend polls the backend for the aggregate data required to render the ["component chart"](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#component-chart) view. This information includes each time the component rendered and how long it took.
299 +
300 +The frontend sends a "_profileComponentDetails_" message specifying which root and commit number it is interested in. The backend sends a response that is serialized in a similar fashion as the Elements tree (above).
301 +
302 +The response consists of the following values:
303 +
304 +1. root id
305 +1. fiber id
306 +1. UTF encoded display name size
307 + * (followed by this number of encoded values)
308 +
309 +Followed by a series of tuples for each time the fiber was committed. The tuples consist of:
310 +
311 +1. commit index
312 +1. duration of time spent rendering the component in this commit
313 +
314 +Here is an example of a fiber that committed twice during a profiling session:
315 +
316 +```js
317 +[
318 + 1, // root id
319 + 2, // fiber id
320 + 4, // UTF encoded display name size
321 + 73, // "I"
322 + 116, // "t"
323 + 101, // "e"
324 + 109, // "m"
325 +
326 + 0, // commit index 0
327 + 11, // actual duration for this fiber in commit 0
328 +
329 + 3, // commit index 3
330 + 7, // actual duration for this fiber in commit 3
331 +]
332 +```
333 +
334 +### Interactions
335 +
336 +The [Interactions chart](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#interactions) shows a time series for every interaction that was traced in the recent profiler session. The frontend sends a "_profileInteractions_" message specifying which root it would like interaction data for. The backend sends a typed array as a response.
337 +
338 +The response always begins with an id that identifies which root the interactions are associated with:
339 +
340 +1. root id
341 +
342 +Next is a series of interactions, consisting of:
343 +
344 +1. interaction id
345 +1. UTF encoded display name size
346 + * (followed by this number of encoded values)
347 +1. Number of commits this interaction was associated with
348 + * (followed by the index of each commit)
349 +
350 +Here is an example of a profiling session consisting of two interactions:
351 +
352 +```js
353 +[
354 + 1, // root id
355 +
356 + 1, // first interaction id
357 + 3, // encoded interaction name size
358 + 70, // "F"
359 + 111, // "o"
360 + 111, // "o"
361 + 2, // number of commits this interaction was associated with
362 + 0, // index of first commit
363 + 3, // index of second commit
364 +
365 + 1, // second interaction id
366 + 3, // encoded interaction name size
367 + 66, // "B"
368 + 97, // "a"
369 + 114, // "r"
370 + 1, // number of commits this interaction was associated with
371 + 0, // index of first commit
372 +]
373 +```
\ No newline at end of file