The biggest selling point of Angular 2 is performance. When developing the new version of Angular, the Angular team state that performance of Angular 2 would be better because of the new the change detection machanism. Angular 2 performance will be faster from 3x to 5x than Angular 1. That’s great with me because the performance of Angular 1 always makes me frustrated. 

So Angular 2 team has released beta version, and the team suggests we can use Angular 2 in production. I think the framework is good enough to make a performance comparasion between Angular 2 and Angular 1. At the time of writing, the latest version of Angular 2 is 2.0.0-beta.3 and latest version of Angular 1 is 1.5.0

The result from this test make me surprise.

The application

This application shows a 30×500 grid, value in each cell of grid will be selected randomly from an array of names. For each 50 miliseconds, it selects 1000 random cells and update value in those cells again.

Angular 2 performance test

Our Angular 2 performance testing app

Implementation

For the Angular 1 version, it has just 1 controller which generate grid data. $interval service executes a function which updates grid every 50 milisecond. In the view of this app, I use ng-repeat to display values from grid. I also use track by to make sure that performance of ng-repeat is okay.

For the Angular 2 version, it just has 1 component. The logic is almost similar with Angular 1 version. In the template, I use NgForOf to display values in grid. At the time of writing, there is not document about optimization for NgForOf.

Let’s see the source code of both applications:

Angular 1 version:

<html ng-app="app">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
        <script>
            angular
                .module('app', [])
                .controller('appCtrl', function($scope, $interval) {
                    var gridWidth = 30;
                    var gridHeight = 500;
                    var interval = 50;
                    var changePerInterval = 10000;
                    var names = ['Joe', 'Michael', 'David', 'Jones', 'Phil', 'Johnson', 'Janes', 'Anna', 'Hugo', 'Lina', 'Scotte', 'Nam', 'Tran',
                                            'Bill', 'Mark', 'Rihanna', 'Justin', 'Bieber', 'James', 'Cameron', 'Peter', 'Anderson', 'Ronaldo', 'Scholes', 'Giggs',
                                            'Lee', 'Cedric', 'Young', 'Zim', 'Hue', 'Zac', 'Jonas', 'Miley', 'Thomas', 'September', 'Green', 'Red', 'Rose', 'Pink'];
                    $scope.grid = createGrid();
                    beginDataChanges();

                    function createGrid() {
                        var grid = [];
                        for (var row = 0; row < gridHeight; row++) {
                            grid[row] = [];
                            for (var column = 0; column < gridWidth; column++) {
                                grid[row].push(getRandomName());
                            }
                        }
                        return grid;
                    }

                    function getRandomNumber(maxBound) {
                        return Math.floor(Math.random() * maxBound);
                    }

                    function getRandomName() {
                        var i = getRandomNumber(names.length);
                        return names[i];
                    }

                    function beginDataChanges() {
                        $interval(function() {
                            for (var i = 0; i < changePerInterval; i++) {
                                var randomRow = getRandomNumber(gridHeight);
                                var randomColumn = getRandomNumber(gridWidth);
                                var name = getRandomName();
                                $scope.grid[randomRow][randomColumn] = name;
                            }
                        }, interval);
                    }
                });
        </script>
    </head>
    <body ng-controller="appCtrl">
        <table>
            <tr ng-repeat="row in grid track by $index">
                <td ng-repeat="name in row track by $index">
                    {{name}}
                </td>
            </tr>
        </table>
    </body>
</html>

Angular 2 version:

index.html

<html>
  <head>
    <title>Angular 2 grid</title>

    <!-- 1. Load libraries -->
    <!-- IE required polyfills, in this exact order -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>

    <script src="https://code.angularjs.org/2.0.0-beta.6/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/tools/system.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.6/Rx.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.6/angular2.dev.js"></script>

    <!-- 2. Configure SystemJS -->
    <script>
      System.config({
        transpiler: 'typescript',
        typescriptOptions: { emitDecoratorMetadata: true }
      });
      System.import('app.ts')
      			.then(null, console.error.bind(console));
    </script>
  </head>
  <body>
    <app>Loading</app>
  </body>
</html>

app.ts:

import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {NgFor} from 'angular2/common';
import {enableProdMode} from 'angular2/core';

enableProdMode();

@Component({
    selector: 'app',
    template: `
        <table>
            <tr *ngFor="#row of grid">
                <td *ngFor="#name of row">
                    {{name}}
                </td>
            </tr>
        </table>
    `,
    directives: [NgFor]
})
export class AppCmp {
    private grid: any;
    private gridWidth = 30;
    private gridHeight = 500;
    private interval = 50;
    private changePerInterval = 10000;
    private names = ['Joe', 'Michael', 'David', 'Jones', 'Phil', 'Johnson', 'Janes', 'Anna', 'Hugo', 'Lina', 'Scotte', 'Nam', 'Tran',
                                    'Bill', 'Mark', 'Rihanna', 'Justin', 'Bieber', 'James', 'Cameron', 'Peter', 'Anderson', 'Ronaldo', 'Scholes', 'Giggs',
                                    'Lee', 'Cedric', 'Young', 'Zim', 'Hue', 'Zac', 'Jonas', 'Miley', 'Thomas', 'September', 'Green', 'Red', 'Rose', 'Pink'];

    constructor() {
        this.grid = this.initializeGrid();
        this.beginDataChanges();
    }

    initializeGrid() {
        var grid = [];
        for (var row = 0; row < this.gridHeight; row++) {
            grid[row] = [];
            for (var column = 0; column < this.gridWidth; column++) {
                grid[row].push(this.getRandomName());
            }
        }
        return grid;
    }

    getRandomNumber(maxBound) {
        return Math.floor(Math.random() * maxBound);
    }

    getRandomName() {
        var i = this.getRandomNumber(this.names.length);
        return this.names[i];
    }

    beginDataChanges() {
        setInterval(() => {
            for (var i = 0; i < this.changePerInterval; i++) {
                var randomRow = this.getRandomNumber(this.gridHeight);
                var randomColumn = this.getRandomNumber(this.gridWidth);
                var name = this.getRandomName();
                this.grid[randomRow][randomColumn] = name;
            }
        }, this.interval);
    }
}

bootstrap(AppCmp);

Live demo for Angular 1 version

Live demo for Angular 2 version

Here is the gif which show the result of both applications side by side. Angular 1 version is on the left and Angular 2 version is on the right.

View post on imgur.com

Even without an actual benchmark, it’s easy to argue that Angular 1 version is the winner at here. I’ve already turned on production mode for Angular 2 app. This result makes me surprise since I believed that Angular 2 should be faster. 

TrackBy comes to the rescue

I asked this question on repository of Angular 2 and receive many some answers. Again, the performance of NgForOf can be improve with TrackBy, same with “track by” in ng-repeat of Angular 1.

Thanks @todoubaba for this plunker, you have made my day:

https://plnkr.co/edit/cjFGtnI704bjSg6F0DEM?p=preview

So after applying TrackBy to NgForOf, performance of Angular 2 is improved dramatically. Although I don’t have benchmark result in number but I feel like the Angular 2 version is the slightly faster.

Conclusion

My conclusion at here: don’t blindly assume everything from document is correct. There will be a lot of tests and trials until we see the real result from a framework, especially when it’s something new. Actually I expect that my test is wrong since I want Angular 2 will be successful. 

How do you think about this test ?