Turn the performance knob to 10

Dr. Gleb Bahmutov PhD

KENSHO

  ―11 

Kensho financial insight

Boston / NYC / SF

Kensho        Angular

Make your app faster

  • Upgrade Angular version

  • Minimize number of watchers

  • Use `track by`

  • Precompute filter expressions

  • Limit DOM to visible elements

How to approach the performance optimization?

Kensho        Angular

Angular app optimization

  • Live app profiling

  • "Angular plus React equals Speed" revisited

  • A story of failure and redemption

Kensho        Angular

PRIMES !!!

Kensho        Angular

"Finding 1000 primes takes a long time ..."

- client

CHROME > DEVTOOLS > SOURCES > SNIPPETS

How many watchers are there?

How long does digest take?

MY PROFILING RULES

PROFILE IN A "CLEAN" BROWSER

PROFILE THE ACTUAL APPLICATION

OPTIMIZE THE TOP BOTTLENECK FIRST

Kensho        Angular

Your application should have "ports" for performance diagnostics

Kensho        Angular

Automatic

car adapter

i.e. Async methods should return / store a promise on the scope

Kensho        Angular

console.profile('action');
$q.when(scope.foo())
  .finally(function () {
    console.profileEnd('action');
  });

Kensho        Angular

Extra code for message-passing actions

Angular 1.x - 15 seconds

React - 0.5 seconds

Idle digest cycle 15ms

Digest cycle runs 1500 times

There are 5000 watchers

Why so many digest cycles?

$scope.cellClicked = function() {
    // Simulate an AJAX request:
    $timeout(function() {
        $scope.status.searchResults = ...;
    }, randomMillis());
}

Do not run digest each time

$scope.cellClicked = function() {
    // Simulate an AJAX request:
    $timeout(function() {
        $scope.status.searchResults = ...;
    }, randomMillis(), false);
}

Table update runs in 0.5 seconds

Do not run digest after each $http

$http.useApplyAsync(true);

Angular grid load: 650ms

React grid load: 150ms

<tr ng-repeat="hour in hours">
    <td ng-repeat="day in days">
        <my-calendar-cell hour="{{hour}}" 
            day="{{day}}"></my-calendar-cell>
    </td>
</tr>

"Without the included ng-repeat Angular 1 would be hailed as the fastest Web framework ever"

- me

Standard ng-repeat tricks

<tr ng-repeat="hour in ::hours track by $index">
    <td ng-repeat="day in ::days track by $index">
        <my-calendar-cell hour="{{::hour}}" 
            day="{{::day}}"></my-calendar-cell>
    </td>
</tr>

Hmm, are we updating DOM on each table cell creation?

boom-repeat

<tr boom-repeat="hour in hours">
    <td boom-repeat="day in days">
        <my-calendar-cell hour="{{hour}}" 
            day="{{day}}"></my-calendar-cell>
    </td>
</tr>

Do not attach the list to the DOM until all items are ready

(benv + jsdom + browserify - Node parts)

Hey, do your part!

N web workers

Load

Angular 1.x app

Rendering parts in N workers

Look at the big picture, not hacks

<ul class="list">
  <li class="person">
    <div class="first">John</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
  <li class="person">
    <div class="first">Mary</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
</ul>

Static prototype

designer

Making an Angular app

<ul class="list">
  <li class="person">
    <div class="first">John</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
  <li class="person">
    <div class="first">Mary</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
</ul>
<ul class="list">
  <li ng-class="..." ng-repeat="person in people">
    <div ng-class="...">{{ person.first | ... }}</div>
    <div ng-class="last">{{ person.last | ... }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>

Live application

designer or developer

Making an Angular app

<ul class="list">
  <li class="person">
    <div class="first">John</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
  <li class="person">
    <div class="first">Mary</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
</ul>
<ul class="list">
  <li ng-class="..." ng-repeat="person in people">
    <div ng-class="...">{{ person.first | ... }}</div>
    <div ng-class="last">{{ person.last | ... }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>

First performance optimization

<ul class="list">
  <li ng-class="computed" ng-repeat="person in people track by person.id">
    <div ng-class="first-computed">{{:: person.first-computed }}</div>
    <div ng-class="last-computed">{{:: person.last-computed }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>

developer

Making an Angular app

<ul class="list">
  <li class="person">
    <div class="first">John</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
  <li class="person">
    <div class="first">Mary</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
</ul>
<ul class="list">
  <li ng-class="..." ng-repeat="person in people">
    <div ng-class="...">{{ person.first | ... }}</div>
    <div ng-class="last">{{ person.last | ... }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>

Second performance optimization

<ul class="list">
  <li ng-class="computed" ng-repeat="person in people track by person.id">
    <div ng-class="first-computed">{{:: person.first-computed }}</div>
    <div ng-class="last-computed">{{:: person.last-computed }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>

good developer

// no need to run global digest
$timeout(..., ms, false);
// combine digests after HTTP
$http.useApplyAsync(true);

Making an Angular app

Making an Angular app

<ul class="list">
  <li class="person">
    <div class="first">John</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
  <li class="person">
    <div class="first">Mary</div>
    <div class="last">Smith</div>
    <img class="avatar" src="..." />
  </li>
</ul>
<ul class="list">
  <li ng-class="..." ng-repeat="person in people">
    <div ng-class="...">{{ person.first | ... }}</div>
    <div ng-class="last">{{ person.last | ... }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>
<ul class="list">
  <li ng-class="computed" ng-repeat="person in people track by person.id">
    <div ng-class="first-computed">{{:: person.first-computed }}</div>
    <div ng-class="last-computed">{{:: person.last-computed }}</div>
    <img ng-class="..." ng-src="..." />
  </li>
</ul>
// no need to run global digest
$timeout(..., ms, false);
// combine digests after HTTP
$http.useApplyAsync(true);

Designer!

Developer

UI / UX

Performance

Making an X app

var HelloMessage = React.createClass({
  render: function() {
    return <div>Hello {this.props.name}</div>;
  }
});
ReactDOM.render(<HelloMessage name="John" />, mountNode);

Developer

UI / UX / Performance

Angular: making quickly the right app

2

Kensho        Angular

Turn the performance knob to 10

  ―11 

Kensho        Angular

Turn the performance knob to 11

By Gleb Bahmutov

Turn the performance knob to 11

Once your Angular application has the features you need, the next step is usually focused on improving its performance, such as the initial load time and responsiveness to user’s commands. The application has to execute quickly in order to be useful. Is the Angular framework fast or slow? I argue that it is very fast, but requires you to know how to measure and optimize the right bottlenecks.

  • 8,714