JS Compilation Test

JavaScript performance comparison

Revision 3 of this test case created by Christopher Froehlich

Info

This test is intended to compare the performance of JavaScript methods as they are compiled by contrasting functions which optimize for Polymorphic parameters vs functions which are intended to consume Monomorphic parameters.

In the Polymorphic case, we have a function called isNullOrEmpty() which is intended to vet the value of any native type and determine whether it is null, undefined, empty ('', {}), or has a length of 0.

In the Monomorphic case, we deterministicly call a function with respect to the type to evaluate.

The premise is that Monomorphic functions will outperform Polymorphic functions as they become "hot" and compiled by the browser to machine code.

Preparation code

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<script>
Benchmark.prototype.setup = function() {
    var Ns = Ns || {};
   
    Ns.isPlainObject = Ns.isPlainObject ||
    function(obj) {
      /// <summary>
      ///    Returns true if the object is a JavaScript object.
      ///     &#10; isPlainObject(Ns.enums.inputTypes) === true
      ///     &#10; isPlainObject('Ns.enums.inputTypes') === false
      /// </summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = (window.$.isPlainObject(obj));
      return ret;
    };
   
    Ns.isJQuery = Ns.isJQuery ||
    function(obj) {
      /// <summary> Returns true if the object jQuery</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = (obj instanceof window.jQuery);
      return ret;
    };
   
    Ns.hasLength = Ns.hasLength ||
    function(obj) {
      /// <summary> Returns true if the object is an Array or jQuery</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = (Ns.isArray(obj) || Ns.isJQuery(obj));
      return ret;
    };
   
    Ns.isGeneric = Ns.isGeneric ||
    function(obj) {
      /// <summary> Returns true if the object is not a function, array, jQuery or JSON object</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = (false === Ns.isFunction(obj) && false === Ns.hasLength(obj) && false === Ns.isPlainObject(obj));
      return ret;
    };
   
    Ns.isNullOrUndefined = Ns.isNullOrUndefined ||
    function(obj) {
      /// <summary> Returns true if the input is null or undefined</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict'; /* Don't attempt to evaluate validity of functions--they're not null */
      var ret = (false === Ns.isFunction(obj));
   
      if (ret) { /* Not a function */
        ret = (obj === null || obj === undefined || ( /* Plain object is an object literal: {} */
        window.$.isPlainObject(obj) && (
        window.$.isEmptyObject(obj)) || false === obj.isValid));
      }
      return ret;
    };
   
    Ns.isTrueOrFalse = Ns.isTrueOrFalse ||
    function(obj) {
      /// <summary> Returns true if a value can be evaluated as true or false.</summary>
      /// <returns type="Boolean" />
      'use strict';
      return (
      obj === true || obj === false || obj === 1 || obj === 0 || obj === 'true' || obj === 'false');
    };
   
    Ns.isNullOrEmpty = Ns.isNullOrEmpty ||
    function(obj, checkLength) {
      /// <summary> Returns true if the input is null, undefined, or ''</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = true;
   
      /* if(obj) is faster, but it coerces type. We also have to check if obj is a truthy value. */
      if (obj || Ns.isTrueOrFalse(obj)) {
        ret = Ns.isNullOrUndefined(obj);
        if (false === ret && Ns.isGeneric(obj)) {
          ret = ((Ns.isString(obj) && obj.trim() === '') || (Ns.isDate(obj) && obj === Ns.dateTimeMinValue) || (Ns.isNumber(obj) && obj === Ns.int32MinVal));
        } else if (checkLength && Ns.hasLength(obj)) {
          ret = (obj.length === 0);
        }
      }
      return ret;
    };
   
    Ns.isFunction = Ns.isFunction ||
    function(obj) {
      'use strict';
      /// <summary> Returns true if the object is a function</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      var ret = ($.isFunction(obj));
      return ret;
    };
   
   
    Ns.isDate = Ns.isDate ||
    function(obj) {
      /// <summary> Returns true if the object is a Date</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      var ret = (obj instanceof Date);
      return ret;
    };
   
    Ns.isString = Ns.isString ||
    function(obj) {
      /// <summary> Returns true if the object is a String object. </summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      var ret = typeof obj === 'string' || Ns.isInstanceOf('string', obj);
      return ret;
    };
   
    Ns.isNumber = Ns.isNumber ||
    function isNumber(obj) {
      /// <summary> Returns true if the object is typeof number</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      var ret = (typeof obj === 'number');
      return ret;
    }
   
    Ns.isNumeric = Ns.isNumeric ||
    function(obj) {
      /// <summary> Returns true if the input can be parsed as a Number </summary>
      /// <param name="str" type="Object"> String or object to test </param>
      /// <returns type="Boolean" />
      var ret = false;
      if (isNumber(obj) && false === Ns.isNullOrEmpty(obj)) {
        var num = +obj;
        if (false === isNaN(num)) {
          ret = true;
        }
      }
      return ret;
    };
   
   
    Ns.isInstanceOf = Ns.isInstanceOf ||
    function(name, obj) {
      'use strict';
      return (Ns.contains(name, obj) && Ns.bool(obj[name]));
    };
   
   
   
    Ns.contains = Ns.contains ||
    function(object, index) {
      /// <summary>Determines whether an object or an array contains a property or index. Null-safe.</summary>
      /// <param name="object" type="Object"> An object to evaluate </param>
      /// <param name="index" type="String"> An index or property to find </param>
      /// <returns type="Boolean" />
      'use strict';
      var ret = false;
      if (false === Ns.isNullOrUndefined(object)) {
        if (Ns.isArray(object)) {
          ret = object.indexOf(index) !== -1;
        }
        if (false === ret && object.hasOwnProperty(index)) {
          ret = true;
        }
      }
      return ret;
    };
   
    Ns.renameProperty = Ns.renameProperty ||
    function(obj, oldName, newName) {
      /// <summary>Renames a property on a Object literal</summary>
      /// <param name="obj" type="Object"> Object containing property </param>
      /// <param name="oldName" type="String"> Current property name </param>
      /// <param name="newName" type="String"> New property name </param>
      /// <returns type="Object"> The modified object </returns>
      'use strict';
      if (false === Ns.isNullOrUndefined(obj) && Ns.contains(obj, oldName)) {
        obj[newName] = obj[oldName];
        delete obj[oldName];
      }
      return obj;
    };
   
    Ns.foundMatch = Ns.foundMatch ||
    function(anObj, prop, value) {
      /// <summary>Determines whether a prop/value match can be found on a property</summary>
      /// <param name="anObj" type="Object"> Object containing property </param>
      /// <param name="prop" type="String"> Property name </param>
      /// <param name="value" type="Object"> Value to match </param>
      /// <returns type="Boolean"> True if succeeded </returns>
      'use strict';
      var ret = false;
      if (false === Ns.isNullOrEmpty(anObj) && Ns.contains(anObj, prop) && anObj[prop] === value) {
        ret = true;
      }
      return ret;
    };
   
    Ns.each = Ns.each ||
    function(thisObj, onSuccess) {
      /// <summary>Iterates an Object or an Array and handles length property</summary>
      /// <param name="thisObj" type="Object"> An object to crawl </param>
      /// <param name="onSuccess" type="Function"> A function to execute on finding a property, which should return true to stop.
      ///<para>if an Array, onSuccess receives (value, key)</para>                                                                               
      ///<para>if an Object, onSuccess receives (thisObject, name, parentObject)</para>
      ///</param>
      /// <returns type="Object">Returns the return of onSuccess</returns>
      'use strict';
      //http://stackoverflow.com/questions/7356835/jquery-each-fumbles-if-non-array-object-has-length-property
      var ret = false,
          childKey, obj, childObj;
      if (Ns.isFunction(onSuccess)) {
        if (Ns.isArray(thisObj)) {
          thisObj.forEach(function(element, index, array) {
            obj = thisObj[index];
            ret = onSuccess(obj, index, thisObj, element);
            return !ret; //false signals break
          });
        } else if (Ns.isPlainObject(thisObj) && false === Ns.contains(thisObj, 'length')) {
          window.$.each(thisObj, function(key, value) {
            obj = thisObj[key];
            ret = onSuccess(obj, key, thisObj, value);
            return !ret; //false signals break
          });
        } else if (Ns.isPlainObject(thisObj)) {
          for (childKey in thisObj) {
            if (Ns.contains(thisObj, childKey)) {
              childObj = thisObj[childKey];
              ret = onSuccess(childObj, childKey, thisObj);
              if (ret) {
                break;
              }
            }
          }
        }
      }
      return ret;
    };
   
    Ns.isArray = Ns.isArray ||
    function isArray(obj) {
      /// <summary> Returns true if the object is an array</summary>
      /// <param name="obj" type="Object"> Object to test</param>
      /// <returns type="Boolean" />
      var ret = ($.isArray(obj));
      return ret;
    };
   
    Ns.isArrayNullOrEmpty = Ns.isArrayNullOrEmpty ||
    function(arr) {
      return (!arr || !arr.length || arr.length === 0 || !arr.push);
    };
   
    Ns.isStringNullOrEmpty = Ns.isStringNullOrEmpty ||
    function(str) {
      return (!str || !str.length || str.length === 0 || !str.toLowerCase);
    };
   
    Ns.isNumberNullOrEmpty = Ns.isNumberNullOrEmpty ||
    function(num) {
      return (!num || isNaN(num) || !num.toPrecision);
    };
   
    Ns.isDateNullOrEmpty = Ns.isDateNullOrEmpty ||
    function(dt) {
      return (!dt || !dt.getTime);
    };
   
    Ns.isObjectNullOrEmpty = Ns.isObjectNullOrEmpty ||
    function(obj) {
      return (!obj);
    };
   
   
    var oneMillion = 1000000;
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Polymorphic
var i, it;
for (i = 0; i < oneMillion; i += 1) {
  it = Math.floor(Math.random() * 7);

  switch (it) {
  case 1:
    Ns.isNullOrEmpty('');
    break;
  case 2:
    Ns.isNullOrEmpty(NaN);
    break;
  case 3:
    Ns.isNullOrEmpty(null);
    break;
  case 4:
    Ns.isNullOrEmpty([]);
    break;
  case 5:
    Ns.isNullOrEmpty({});
    break;
  case 6:
    Ns.isNullOrEmpty(false);
    break;
  }
}
pending…
Polymorphic (with hints)
var i, it;
for (i = 0; i < oneMillion; i += 1) {
  it = Math.floor(Math.random() * 7);

  switch (it) {
  case 1:
    var str = '';
    Ns.isNullOrEmpty(str);
    break;
  case 2:
    var num = NaN;
    Ns.isNullOrEmpty(num);
    break;
  case 3:
    var date = null;
    Ns.isNullOrEmpty(date);
    break;
  case 4:
    var arr = [];
    Ns.isNullOrEmpty(arr);
    break;
  case 5:
    var obj = {};
    Ns.isNullOrEmpty(obj);
    break;
  case 6:
    var bool = false;
    Ns.isNullOrEmpty(bool);
    break;
  }
}
pending…
Monomorphic
var i, it;

for (i = 0; i < oneMillion; i += 1) {
  it = Math.floor(Math.random() * 6);

  switch (it) {
  case 1:
    Ns.isStringNullOrEmpty('');
    break;
  case 2:
    Ns.isNumberNullOrEmpty(NaN);
    break;
  case 3:
    Ns.isDateNullOrEmpty(null);
    break;
  case 4:
    Ns.isArrayNullOrEmpty([]);
    break;
  case 5:
    Ns.isObjectNullOrEmpty({});
    break;
  }
}
pending…
Monomorphic (with hints)
var i, it;

for (i = 0; i < oneMillion; i += 1) {
  it = Math.floor(Math.random() * 6);

  switch (it) {
  case 1:
    var str = '';
    Ns.isStringNullOrEmpty(str);
    break;
  case 2:
    var num = NaN;
    Ns.isNumberNullOrEmpty(num);
    break;
  case 3:
    var date = null;
    Ns.isDateNullOrEmpty(date);
    break;
  case 4:
    var arr = [];
    Ns.isArrayNullOrEmpty(arr);
    break;
  case 5:
    var obj = {};
    Ns.isObjectNullOrEmpty(obj);
    break;
  }
}
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment