15

We're building an Angular app, and we're trying to figure out how to get some benchmarks on how long it takes to render various pages. I've read about performance.timing here, but that seems to only be useful with non-single page applications as when I navigate to new views in our app, the timing numbers do not change.

Ideally, we'd be able to insert some code that gets render time for various views and posts it our our Big Query service.

Any ideas on how to get some timing information for views in an Angular app?

EDIT:

More specifically, you go to a route which loads a big ng-repeat list (which is not optimal for performance), and the window has a long delay before it actually renders the items in the list. We'd like to see how long it takes to go from the big blank view to rendering the items in the list.

Community
  • 1
  • 1
EmptyArsenal
  • 7,314
  • 4
  • 33
  • 56
  • 1
    What exactly would you like to measure? There are some built-in events out there such as `$routeChangeStart` and `$routeChangeSuccess`. May be you could subscribe them and use the [User Timing API](http://www.html5rocks.com/en/tutorials/webperformance/usertiming/) to record the timing. – runTarm Aug 14 '14 at 15:52
  • I'll have to see what exactly those events are capturing. I added an edit above with more specifics. – EmptyArsenal Aug 14 '14 at 16:00
  • might be resemble to - http://stackoverflow.com/a/1975103/104380 – vsync Aug 14 '14 at 16:02
  • @runTarm Through your reference, I found `$viewContentLoaded` which might be what I'm looking for! – EmptyArsenal Aug 14 '14 at 16:03
  • Still unresolved. `$viewContentLoaded` fires immediately when we go to a route rather than when the content is actually rendered. – EmptyArsenal Aug 14 '14 at 17:01

3 Answers3

14

TypeScript / Angular (2+) answer:

To benchmark the time spent in Angular rendering the component tree, measure before and after change detection. This will time how long it takes Angular to create all the child components and evaluate ngIfs / ngFors etc.

Sometimes when change detection runs, nothing will have changed and the timed interval will be small. Just look for the longest time interval and that's probably where all the work is happening. Also, best to use OnPush change detection strategy for this. See http://blog.angular-university.io/onpush-change-detection-how-it-works/

Declare this Timer utility above your @Component({...}) class :

class Timer {
  readonly start = performance.now();

  constructor(private readonly name: string) {}

  stop() {
    const time = performance.now() - this.start;
    console.log('Timer:', this.name, 'finished in', Math.round(time), 'ms');
  }
}

Add this inside your component class:

private t: Timer;

// When change detection begins
ngDoCheck() {
  this.t = new Timer('component 1 render');
}

// When change detection has finished:
// child components created, all *ngIfs evaluated
ngAfterViewChecked() {
  this.t.stop();  // Prints the time elapsed to the JS console.
}

Note that Angular rendering is separate from the browser rendering (painting) on the screen. That takes time too. When you see javascript (yellow) with lots of "checkAndUpdateView" and "callViewAction" calls in the Chrome Timeline Tool (javascript developer console -> performance -> record) that is Angular rendering happening. When you see purple in the Chrome Timeline Tool, labelled "rendering", that is actual browser rendering.

Alexander Taylor
  • 16,574
  • 14
  • 62
  • 83
10

After doing some experimentation, I found a way to get an approximation how long a view takes to render.

The scenario:

  1. Route to a page in an Angular app
  2. Make AJAX call to get a lengthy list of items
  3. Take list of items, pass into an ng-repeat ul
  4. View rendered list

In an Angular app, ng-repeat is a notorious performance killer, but it's extremely convenient. Here's a discussion on the performance and here.

Let's say that we want to get an approximation of the time it takes to get from #3 to #4, as testing the AJAX call performance is pretty straightforward.

Here's some context:

<ul>
  <li ng-repeat="item in items">
    {{item.name}}
    <!-- More content here -->
  </li>
</ul>

And inside the Angular controller:

// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
  }, function (reason) {
    console.log(reason);
  });

The events mentioned by @runTarm, $routeChangeStart, $routeChangeSuccess, and $viewContentLoaded fire as soon as the route is loaded, but before the DOM renders the items, so they don't solve the issue. However, through experimentation, I found that once the AJAX callback finishes and sets $scope.items, Angular begins a blocking operation of processing items and preparing the ng-repeat ul to be inserted into the DOM. Thus, if you get the time after your AJAX call finishes, and get the time again in a setTimeout specified in the callback, the setTimeout callback will be queued to wait until Angular is done with the repeater process and get you the time a split second before the DOM renders, giving you the closest approximation for rendering time. It won't be actual render time, but the slow part for us is not the DOM doing it's work, but Angular, which is what we're looking to measure.

Here's the revised example:

// Get items to populate ng-repeater
MyApiService.getItems(query)
  .then( function (data) {
    var start = new Date();
    $scope.items = data;
    // When callback finishes, Angular will process items
    // and prepare to load into DOM
    setTimeout( function () {
      // Logs when Angular is done processing repeater
      console.log('Process time: ' + (new Date() - start));
    }); // Leave timeout empty to fire on next tick
  }, function (reason) {
    console.log(reason);
  });
EmptyArsenal
  • 7,314
  • 4
  • 33
  • 56
  • 4
    Apart from recording the timing yourself, you might find the Flame chart feature of Chrome Devtools to be very useful! https://developer.chrome.com/devtools/docs/cpu-profiling – runTarm Aug 15 '14 at 18:31
0

Try the open source Chrome plugin Angular-performance, you can monitor the digest time in Angular applications: https://github.com/Linkurious/angular-performance

Seb
  • 618
  • 5
  • 11