Webworker delegation

JavaScript performance comparison

Revision 3 of this test case created by Jasper

Preparation code

<script>
function stringify(obj) {
        var props;

        if ( Array.isArray(obj) ){
            props = [];
            for ( var i = 0, l = obj.length; i < l; ++i ){
                props.push( stringify(obj[ i ]) );
            }
            return '[' + props.join(',') + ']';
        } else if ( typeof obj !== 'object' ){
            return obj.toString();
        } else {
            props = [];
            for (var key in obj) {
                props.push(key + ':' + stringify(obj[key]));
            }
            return '{' + props.join(',') + '}';
        }
    }

    function Batch(obj) {

        this.init(obj);
    }

    Batch.prototype = {
        init: function (obj) {

            this.init = false;
            // start false, so that check callbacks only executes callbacks if at least one map is called/completed
            this.jobs = false;
            this.callbacks = [];
            this.results = [];

            function setup(obj) {

                self.onmessage = function (e) {

                    self.postMessage( obj.process( e.data.args ) );
                    self.close();
                };
            }

            var blob = new Blob(['!' + setup.toString() + '(' + stringify(obj) + ')']);
            this.blobUrl = window.URL.createObjectURL(blob);
        },

        run: function (args, buffers) {
            var self = this;
            var worker = new Worker(this.blobUrl);
            self.jobs++;
            worker.onmessage = function (e) {
                self.results.push(e.data);
                self.jobs--;
                self.checkFinish();
            };

            worker.postMessage({
                args: args
            }, buffers);
            return this;
        },

        finish: function (fn) {
            if (!fn) {
                return this;
            }
            this.callbacks.push(fn);
            this.checkFinish();
            return this;
        },

        checkFinish: function () {
            if (this.jobs !== 0) {
                return this;
            }

            var fn;

            while (fn = this.callbacks.shift()) {
                fn.call(this, this.results);
            }
            return this;
        },

        delegate: function( data, chunks, transfer ){

            var chunksize = (data.length / chunks)|0;

            if ( transfer ){

                var offset = 0;
                var sub;
                var arr;
                while ( offset < data.length ){
                    sub = data.subarray(offset, offset + chunksize);
                    arr = new (data.constructor)( sub );
                    this.run( arr, [arr.buffer] );
                    offset += chunksize;
                }

            } else {

                while ( data.length ){
                   
                    this.run( data.splice(0, chunksize) );
                }
            }
        }

    };

function myHelper( x ){
    return x * x;
}

function fib(n) {
      return n < 2 ? 1 : fib(n - 1) + fib(n - 2);
    };

var schema = {
   
    foo: 2,
    bar: [ 1, 2, 3 ],
    baz: {
        n: 10
    },
   
    fib: fib,
   
    process: function( chunk ){
       
        var results = new Float64Array( chunk.length );
       
        for ( var i = 0, l = chunk.length; i < l; ++i ){
           
            results[i] = this.fib( chunk[i] );
        }
       
        return results;
    }
};

</script>
<script>
Benchmark.prototype.setup = function() {
    var l = 1000;
    var bigData = new Float64Array(l);
    while( l-- ){
        bigData[l] = Math.floor(Math.random() * 10 + 10);
    }
   
    function reduce( results ){
        var ans = 0;
       
        for ( var i = 0, l = results.length; i < l; ++i ){
            for ( var j = 0, ll = results[ i ].length; j < ll; ++j ){
               
                ans += results[ i ][ j ];
            }
        }
    }
   
    var alg = new Batch( schema );
};
</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
Inline (chunks: 4)
var data = bigData;
var results = [];
var chunks = 4;
var chunksize = (data.length / chunks)|0;
       
        var offset = 0;
                var sub;
                var arr;
                while ( offset < data.length ){
                    sub = data.subarray(offset, offset + chunksize);
                    arr = new (data.constructor)( sub );
                    results.push(schema.process( arr ));
                    offset += chunksize;
                }
 
pending…
Async (chunks: 4)
// async test
alg.delegate( bigData, 4, true );
alg.finish( function(){
    deferred.resolve();
});
pending…
Async (chunks: 8)
// async test
alg.delegate( bigData, 8, true );
alg.finish( function(){
    deferred.resolve();
});
pending…
Async (chunks: 16)
// async test
alg.delegate( bigData, 16, true );
alg.finish( function(){
    deferred.resolve();
});
pending…
Async (chunks: 1)
// async test
alg.delegate( bigData, 1, true );
alg.finish( function(){
    deferred.resolve();
});
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