orderBy speed optimization (small) (v2)

Revision 2 of this benchmark created by Brian Ford on


Description

Same as the first version, but with initially unsorted data.

Use shwartizian transform (http://en.wikipedia.org/wiki/Schwartzian_transform) for orderBy optimization.

Preparation HTML

<script src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script>

<div ng-app="myApp"></div>

Setup

// only for test purposes, as orderBy code is core code
var isArray = angular.isArray;
var toBoolean = Boolean;

Benchmark.prototype.setup = function() {
  window.list = [];
  
  for (var i = 0; i < 4000; i++) {
    window.list.push({
      title: 'item' + Math.random(),
      properties: { amount: Math.random() }
    });
  }
};

angular.module('myApp', [])
.filter('orderByImproved', ['$parse', function($parse) {

return function(array, sortPredicate, reverseOrder) {
    if (!isArray(array)) return array;
    if (!sortPredicate) return array;
    sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];

    var sortPredicate = sortPredicate.map(function(predicate){
      var descending = false, get = predicate || identity;
      if (angular.isString(predicate)) {
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
          descending = predicate.charAt(0) == '-';
          predicate = predicate.substring(1);
        }
        get = $parse(predicate);
      }
      return {
        getter: get,
        comparator: reverseComparator(function(a, b) {
          return compare(a, b);
        }, descending)
      };
    });

    // http://en.wikipedia.org/wiki/Schwartzian_transform
    var out = array
      .map(function(item) {
        return {
          item: item,
          predicate: sortPredicate.map(function(obj) {
            return obj.getter(item);
          })
        };
      })
      .sort(reverseComparator(comparator, reverseOrder))
      .map(function(obj) {
        return obj.item;
      });

    return out;

    function comparator(o1, o2){
      for ( var i = 0; i < sortPredicate.length; i++) {
        var comp = sortPredicate[i].comparator(
          o1.predicate[i],
          o2.predicate[i]
        );
        if (comp !== 0) return comp;
      }
      return 0;
    }
    function reverseComparator(comp, descending) {
      return toBoolean(descending)
          ? function(a,b){return comp(b,a);}
          : comp;
    }
    function compare(v1, v2){
      var t1 = typeof v1;
      var t2 = typeof v2;
      if (t1 == t2) {
        if (t1 == "string") {
           v1 = v1.toLowerCase();
           v2 = v2.toLowerCase();
        }
        if (v1 === v2) return 0;
        return v1 < v2 ? -1 : 1;
      } else {
        return t1 < t2 ? -1 : 1;
      }
    }
  }
}]);

Test runner

Ready to run.

Testing in
TestOps/sec
Original orderBy
angular.injector(['ng', 'myApp'])
.get('$filter')('orderBy')(list, '-properties.amount');
ready
Improved orderBy
angular.injector(['ng', 'myApp'])
.get('$filter')('orderByImproved')(list, '-properties.amount');
ready
Original orderBy (warm)
angular.injector(['ng', 'myApp'])
.get('$filter')('orderBy')(list, '-properties.amount');
ready
Improved orderBy (warm)
angular.injector(['ng', 'myApp'])
.get('$filter')('orderByImproved')(list, '-properties.amount');
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by agentcooper on
  • Revision 2: published by Brian Ford on
  • Revision 3: published on