(Posting this on behalf of Jonathan Carter who authored this post)
Performance is a critical aspect of any 5-star rated app. Users expect pages to load quickly, interactions to respond immediately and animations to be fluid. While it’s easy to recognize the importance of these qualities, achieving them can often prove challenging, especially with the tight demands they impose (e.g. 60 FPS requires <16ms per frame).
The key to maintaining a responsive app is keeping the UI thread as free as possible. This way, it will be ready to respond to user-interaction (e.g. button click, scrolling) as soon as it occurs and will be able to maintain a fast and consistent frequency when producing new frames within an animation/game.
- Parsing HTML content and constructing the respective DOM elements
- Parsing CSS content
- Determining the style properties for each DOM element (style cascading)
- Determining element size/position (layout)
- Determining which objects are no longer needed and freeing their associated memory (garbage collection)
- Rendering new content to the screen (IE11 adds support for “independent rendering” that moves this step to another thread)
Getting started with the HTML UI Responsiveness tool
You can begin a new profiling session by selecting the checkbox and clicking the Start button. At this point, you can return to your app and exercise the scenario you’d like to investigate. When completed, return to Visual Studio and click the Stop collection link (or Stop button) to end your profiling session and begin analyzing the collected data.
Once the analysis has completed, you’ll be presented with a performance report that you can begin investigating for optimization opportunities.
Additionally, the timeline also indicates the frame rate that your app was achieving throughout the duration of the session, which makes it easier to understand how CPU utilization was affecting the ability to maintain a consistent visual throughput. This makes it possible to spot “dips” in your frame rate and correlate them to the offending operations.
Interacting with the timeline details
Once you’ve identified a region of interest within the session (based on examining either the CPU utilization and/or Visual throughput graphs), you can investigate that period of time by click-and-dragging that selection on either of the graphs (or the ruler). As you change your selection, the Timeline details will automatically update to reflect this filtered view. If you need to adjust your selection to include more or less time, you can select the handles on the ruler and use the left/right arrow keys to achieve more fine-grained control.
When you find an event of interest, you can further investigate it using the following gestures:
- Selecting the event row, which will display additional details about it in the detail pane (the area to the right of the Gantt chart). For many events, this extra data assists in identifying the event and understanding why it occurred (e.g. the type of event, the elapsed time of a timer)
- Expanding the event row (if applicable), which will display the “child” events it synchronously triggered (e.g. a DOM event triggering HTML parsing due to setting innerHTML on a DOM element)
By default, the timeline is sorted sequentially, which means that events that occurred earlier in time will appear higher (and to the left) than events which occurred later in the current selection. If you’re interested in identifying the most expensive events (based on inclusive duration), you can change the Sort by option to “Duration (inclusive)” and the timeline will immediately bubble up the “hot paths” that contributed the most to your bottleneck.
There are many event types, lots of operation metadata and numerous combinations of parent/child relationships (e.g. HTML parsing triggering Script evaluation, a DOM operation triggering Paint, Timer triggering CSS parsing). The data can be vast, but this expressive profiling information is what makes the tool so powerful and allows you to make informed optimizations.
To illustrate the types of rich data/patterns that the tool surfaces, the following are some unique scenarios that it allows you to identify:
2. Event-driven causality around why script is running (e.g. event handler, timer, requestAnimationFrame callback), along with the identifiable metadata about the event, and source navigation capabilities, which make it easier to optimize user-interaction
Example showing the target element, callback function name and source location of a specific DOM event
3. DOM calls made within script, along with their synchronous rendering side-effects (if any)
Example showing how calling getComputedStyle on the specific <div> triggered an inline layout
4. DOM elements that were affected in a specific layout/style calculation/paint pass, along with the exclusive time they contributed to the operation, which makes it easier to optimize rendering costs
Example showing the set of elements that required their layout to be re-computed
5. Much much more!
Measuring an isolated scenario
In order to make a profiling session efficient and actionable, you want to scope down the area of investigation as far as possible. This way, you’re not trying to interpret data that doesn’t actually relate to the problem you’re trying to solve. Because this capability is so critical to the typical performance analysis workflow, the HTML UI Responsiveness tool allows you to instrument your app with “milestones of interest”/scenario boundaries which can significantly help provide the context needed to decide where in the trace you should focus.
To make use of these markers, you simply add calls to the performance.mark method at the relevant locations within your codebase. When you subsequently profile the app, these calls will show up as marks (the legend refers to them as “User marks” because their app-specific) on the ruler.
With your scenario-specific marks in place, you can easily identify the period of interest on the graphs, select its timespan and begin an isolated investigation. Additionally, the ruler within the Timeline details also display your marks, which allows you to see the exact events which occurred during the problematic period you’ve selected.
To help bootstrap your investigations in lieu of having any app-specific markers (or supplement your custom marks), the tool also displays ruler marks which represent fundamental events that occured during a profiling session (called “App lifecycle events”).
The following event types are currently supported:
- DOMContentLoaded – Helps identify at what point a page’s synchronous sub resources (stylesheets and non/async or deferred scripts) were fully downloaded and the DOM tree has been constructed
- Load – Helps identify at what point a page’s entire sub resources (including images) were fully downloaded and all DOMContentLoaded/Load event handlers have completed
- Navigation – Helps identify at what point a page navigation/refresh occurred
In some cases, these marks alone can help isolate specific scenarios. For example, being able to identify the period of time between a navigation and the subsequent DOMContentLoaded event would allow you to investigate the cost of sub-resources being loaded and how they individually affect page load time.
We want to hear from you!
The HTML UI Responsiveness tool provides a lot of power and visibility into data that was otherwise difficult to ascertain. We hope this allows you to make more informed optimizations regarding your app’s performance, which will ultimately contribute to its success.
Most importantly, however, we want to hear from you! If you get a chance to evaluate the tool, please let us know how your experience went, as well as any feedback/suggestions/complaints you may have. You can reach out by means of UserVoice, Connect, Twitter (mention “HTML UI Responsiveness”), or by asking questions in the MSDN diagnostics forum.