Skip to content

Commit 4ec4dca

Browse files
committed
feat(docs): document change detection profiler
1 parent e9ad100 commit 4ec4dca

File tree

3 files changed

+278
-1
lines changed

3 files changed

+278
-1
lines changed

TOOLS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Developer Tools for Angular 2
22

3-
- JavaScript (TBD)
3+
- [JavaScript](TOOLS_JS.md)
44
- [Dart](TOOLS_DART.md)

TOOLS_DART.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@
33
Here you will find a collection of tools and tips for keeping your application
44
perform well and contain fewer bugs.
55

6+
## Angular debug tools in the dev console
7+
8+
Angular provides a set of debug tools that are accessible from any browser's
9+
developer console. In Chrome the dev console can be accessed by pressing
10+
Ctrl + Shift + j.
11+
12+
### Enabling debug tools
13+
14+
By default the debug tools are disabled. You can enable debug tools as follows:
15+
16+
```dart
17+
import 'package:angular2/tools.dart';
18+
19+
main() {
20+
var appRef = await bootstrap(Application);
21+
enableDebugTools(appRef);
22+
}
23+
```
24+
25+
### Using debug tools
26+
27+
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
28+
top level object is called `ng` and contains more specific tools inside it.
29+
30+
Example:
31+
32+
```javascript
33+
ng.profiler.timeChangeDetection();
34+
```
35+
636
## Code size
737

838
Code needs to be downloaded, parsed and executed. Too much code could lead to
@@ -145,3 +175,110 @@ transformers:
145175
- --trust-type-annotations
146176
- --trust-primitives
147177
```
178+
179+
## Performance
180+
181+
### Change detection profiler
182+
183+
If your application is janky (it misses frames) or is slow according to other
184+
metrics it is important to find the root cause of the issue. Change detection
185+
is a phase in Angular's lifecycle that detects changes in values that are
186+
bound to UI, and if it finds a change it performs the corresponding UI update.
187+
However, sometimes it is hard to tell if the slowness is due to the act of
188+
computing the changes being slow, or due to the act of applying those changes
189+
to the UI. For your application to be performant it is important that the
190+
process of computing changes is very fast. For best results it should be under
191+
3 milliseconds in order to leave room for the application logic, the UI updates
192+
and browser's rendering pipeline to fit withing the 16 millisecond frame
193+
(assuming the 60 FPS target frame rate).
194+
195+
Change detection profiler repeatedly performs change detection without invoking
196+
any user actions, such as clicking buttons or entering text in input fields. It
197+
then computes the average amount of time it took to perform a single cycle of
198+
change detection in milliseconds and prints it to the console. This number
199+
depends on the current state of the UI. You will likely see different numbers
200+
as you go from one screen in your application to another.
201+
202+
#### Running the profiler
203+
204+
Enable debug tools (see above), then in the dev console enter the following:
205+
206+
```javascript
207+
ng.profiler.timeChangeDetection();
208+
```
209+
210+
The results will be printed to the console.
211+
212+
#### Recording CPU profile
213+
214+
Pass `{record: true}` an argument:
215+
216+
```javascript
217+
ng.profiler.timeChangeDetection({record: true});
218+
```
219+
220+
Then open the "Profiles" tab. You will see the recorded profile titled
221+
"Change Detection". In Chrome, if you record the profile repeatedly, all the
222+
profiles will be nested under "Change Detection".
223+
224+
#### Interpreting the numbers
225+
226+
In a properly-designed application repeated attempts to detect changes without
227+
any user actions should result in no changes to be applied on the UI. It is
228+
also desirable to have the cost of a user action be proportional to the amount
229+
of UI changes required. For example, popping up a menu with 5 items should be
230+
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
231+
change detection with no UI updates should be as fast as possible. Ideally the
232+
number printed by the profiler should be well below the length of a single
233+
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
234+
235+
#### Investigating slow change detection
236+
237+
So you found a screen in your application on which the profiler reports a very
238+
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
239+
recording while profiling:
240+
241+
```javascript
242+
ng.profiler.timeChangeDetection({record: true});
243+
```
244+
245+
Then look for hot spots using
246+
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
247+
248+
#### Reducing change detection cost
249+
250+
There are many reasons for slow change detection. To gain intuition about
251+
possible causes it would help to understand how change detection works. Such a
252+
discussion is outside the scope of this document (TODO link to docs), but here
253+
are some key concepts in brief.
254+
255+
By default Angular uses "dirty checking" mechanism for finding model changes.
256+
This mechanism involves evaluating every bound expression that's active on the
257+
UI. These usually include text interpolation via `{{expression}}` and property
258+
bindings via `[prop]="expression"`. If any of the evaluated expressions are
259+
costly to compute they could contribute to slow change detection. A good way to
260+
speed things up is to use plain class fields in your expressions and avoid any
261+
kinds of computation. Example:
262+
263+
```dart
264+
@View(
265+
template: '<button [enabled]="isEnabled">{{title}}</button>'
266+
)
267+
class FancyButton {
268+
// GOOD: no computation, just return the value
269+
bool isEnabled;
270+
271+
// BAD: computes the final value upon request
272+
String _title;
273+
String get title => _title.trim().toUpperCase();
274+
}
275+
```
276+
277+
Most cases like these could be solved by precomputing the value and storing the
278+
final value in a field.
279+
280+
Angular also supports a second type of change detection - the "push" model. In
281+
this model Angular does not poll your component for changes. Instead, the
282+
component "tells" Angular when it changes and only then does Angular perform
283+
the update. This model is suitable in situations when your data model uses
284+
observable or immutable objects (also a discussion for another time).

TOOLS_JS.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Developer Tools for JavaScript
2+
3+
Here you will find a collection of tools and tips for keeping your application
4+
perform well and contain fewer bugs.
5+
6+
## Angular debug tools in the dev console
7+
8+
Angular provides a set of debug tools that are accessible from any browser's
9+
developer console. In Chrome the dev console can be accessed by pressing
10+
Ctrl + Shift + j.
11+
12+
### Enabling debug tools
13+
14+
By default the debug tools are disabled. You can enable debug tools as follows:
15+
16+
```typescript
17+
import 'angular2/tools';
18+
19+
bootstrap(Application).then((appRef) => {
20+
enableDebugTools(appRef);
21+
});
22+
```
23+
24+
### Using debug tools
25+
26+
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
27+
top level object is called `ng` and contains more specific tools inside it.
28+
29+
Example:
30+
31+
```javascript
32+
ng.profiler.timeChangeDetection();
33+
```
34+
35+
## Performance
36+
37+
### Change detection profiler
38+
39+
If your application is janky (it misses frames) or is slow according to other
40+
metrics it is important to find the root cause of the issue. Change detection
41+
is a phase in Angular's lifecycle that detects changes in values that are
42+
bound to UI, and if it finds a change it performs the corresponding UI update.
43+
However, sometimes it is hard to tell if the slowness is due to the act of
44+
computing the changes being slow, or due to the act of applying those changes
45+
to the UI. For your application to be performant it is important that the
46+
process of computing changes is very fast. For best results it should be under
47+
3 milliseconds in order to leave room for the application logic, the UI updates
48+
and browser's rendering pipeline to fit withing the 16 millisecond frame
49+
(assuming the 60 FPS target frame rate).
50+
51+
Change detection profiler repeatedly performs change detection without invoking
52+
any user actions, such as clicking buttons or entering text in input fields. It
53+
then computes the average amount of time it took to perform a single cycle of
54+
change detection in milliseconds and prints it to the console. This number
55+
depends on the current state of the UI. You will likely see different numbers
56+
as you go from one screen in your application to another.
57+
58+
#### Running the profiler
59+
60+
Enable debug tools (see above), then in the dev console enter the following:
61+
62+
```javascript
63+
ng.profiler.timeChangeDetection();
64+
```
65+
66+
The results will be printed to the console.
67+
68+
#### Recording CPU profile
69+
70+
Pass `{record: true}` an argument:
71+
72+
```javascript
73+
ng.profiler.timeChangeDetection({record: true});
74+
```
75+
76+
Then open the "Profiles" tab. You will see the recorded profile titled
77+
"Change Detection". In Chrome, if you record the profile repeatedly, all the
78+
profiles will be nested under "Change Detection".
79+
80+
#### Interpreting the numbers
81+
82+
In a properly-designed application repeated attempts to detect changes without
83+
any user actions should result in no changes to be applied on the UI. It is
84+
also desirable to have the cost of a user action be proportional to the amount
85+
of UI changes required. For example, popping up a menu with 5 items should be
86+
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
87+
change detection with no UI updates should be as fast as possible. Ideally the
88+
number printed by the profiler should be well below the length of a single
89+
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
90+
91+
#### Investigating slow change detection
92+
93+
So you found a screen in your application on which the profiler reports a very
94+
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
95+
recording while profiling:
96+
97+
```javascript
98+
ng.profiler.timeChangeDetection({record: true});
99+
```
100+
101+
Then look for hot spots using
102+
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
103+
104+
#### Reducing change detection cost
105+
106+
There are many reasons for slow change detection. To gain intuition about
107+
possible causes it would help to understand how change detection works. Such a
108+
discussion is outside the scope of this document (TODO link to docs), but here
109+
are some key concepts in brief.
110+
111+
By default Angular uses "dirty checking" mechanism for finding model changes.
112+
This mechanism involves evaluating every bound expression that's active on the
113+
UI. These usually include text interpolation via `{{expression}}` and property
114+
bindings via `[prop]="expression"`. If any of the evaluated expressions are
115+
costly to compute they could contribute to slow change detection. A good way to
116+
speed things up is to use plain class fields in your expressions and avoid any
117+
kinds of computation. Example:
118+
119+
```typescript
120+
@View({
121+
template: '<button [enabled]="isEnabled">{{title}}</button>'
122+
})
123+
class FancyButton {
124+
// GOOD: no computation, just return the value
125+
isEnabled: boolean;
126+
127+
// BAD: computes the final value upon request
128+
_title: String;
129+
get title(): String { return this._title.trim().toUpperCase(); }
130+
}
131+
```
132+
133+
Most cases like these could be solved by precomputing the value and storing the
134+
final value in a field.
135+
136+
Angular also supports a second type of change detection - the "push" model. In
137+
this model Angular does not poll your component for changes. Instead, the
138+
component "tells" Angular when it changes and only then does Angular perform
139+
the update. This model is suitable in situations when your data model uses
140+
observable or immutable objects (also a discussion for another time).

0 commit comments

Comments
 (0)