grunt-benchmark

Grunt task for benchmarking

grunt-benchmark

Grunt task for benchmarking with Benchmark.js.

Install this grunt plugin next to your project's Gruntfile with: npm install grunt-benchmark

Then add this line to your project's Gruntfile:

grunt.loadNpmTasks('grunt-benchmark');

Create a benchmarks/ folder and create a benchmark script within that folder, ie fibonacci.js:

var fibonacci = function(n) {
  return n < 2 ? n : fibonacci(- 1) + fibonacci(- 2);
};
 
module.exports = function() {
  fibonacci(10);
};

The setup your Gruntfile config to run the benchmarks within the benchmarks/ folder:

grunt.initConfig({
  benchmark: {
    all: {
      src: ['benchmarks/*.js'],
      dest: 'benchmarks/results.csv'
    }
  }
});

Then run the task:

$ grunt benchmark
Running "benchmark:all" (benchmark) task
Benchmarking "0" [benchmarks/test-timeout.js] x10...
>> test-timeout x 418,070 ops/sec ±12.73% (46 runs sampled)

Benchmark name, date, times and per iteration will be logged in a csv format.

You can add test options to pass to Benchmark.js by exporting an object of [test options].

module.exports = {
  name: 'Timeout (asynchronous)',
  maxTime: 2,
  defer: true,
  onCompletefunction() {
    console.log('Hooray!');
  },
  fnfunction(deferred) {
    setTimeout(function() {
      deferred.resolve();
    }, 500);
  }
};

Result:

$ grunt benchmark
Running "benchmark:singleTest" (benchmark) task
Benchmarking "Timeout (asynchronous)" [benchmarks/singleTest.js]...
Hooray!
>> Timeout (asynchronous) x 2.00 ops/sec ±0.14% (8 runs sampled)

You can pit implementations against one another by creating a test suite.

var fibonacci = function(n) {
  return n < 2 ? n : fibonacci(- 1) + fibonacci(- 2);
};
 
var fibonacci_memoized = (function (  ) {
  var memo = [0, 1];
  var fib = function (n) {
    var result = memo[n];
    if (typeof result !== 'number') {
      result = fib(- 1) + fib(- 2);
      memo[n] = result;
    }
    return result;
  };
  return fib;
}());
 
// A test suite 
module.exports = {
  name: 'Fibonacci Showdown',
  tests: {
    'Fibonacci'function() {
      fibonacci(10);
      fibonacci(5);
    },
    'Fibonacci2'function() {
      fibonacci_memoized(10);
      fibonacci_memoized(5);
    }
  }
};

Result:

$ grunt benchmark
Running "benchmark:fibonacci" (benchmark) task
Benchmarking suite "Fibonacci" [benchmarks/fibonacci.js]...
>> fibonacci x 13,386,628 ops/sec ±8.63% (74 runs sampled)
>> fibonacci_memoized x 30,509,658 ops/sec ±2.10% (89 runs sampled)
Fastest is fibonacci_memoized

Set exports.tests to an Object that maps test names to functions and or [Benchmark.js test options].

module.exports = {
  name: 'Timeout Showdown',
  tests: {
    'Return immediately (synchronous)'function() {
      return;
    },
    'Timeout: 50ms (asynchronous)': {
      defer: true,
      fnfunction(deferred) {
        setTimeout(deferred.resolve, 50);
      }
    },
    'Timeout: 100ms (asynchronous)': {
      defer: true,
      fnfunction(done) {
        setTimeout(deferred.resolve, 100);
      }
    }
  }
};

Set exports.tests to an Array of functions and or [Benchmark.js test options].

module.exports = {
  name: 'Timeout Showdown',
  tests: [
    {
      name: 'Return immediately (synchronous)',
      fnfunction() {
        return;
      }
    },
    {
      name: 'Timeout: 50ms (asynchronous)',
      defer: true,
      fnfunction(done) {
        setTimeout(done, 50);
      }
    },
    {
      name: 'Timeout: 100ms (asynchronous)',
      defer: true,
      fnfunction(done) {
        setTimeout(done, 100);
      }
    }
  ]
};

Included is a helper, spawnTask, for running Grunt tasks within your benchmarks. This example will create a function to run the watch task:

// benchmarks/watch.js 
// Create a spawnable watch task. Doesn't actually spawn until called. 
var watchTask = require('grunt-benchmark').spawnTask('watch', {
 
  // Text trigger to look for to know when to run the next step or exit 
  trigger: 'Waiting...',
 
  // Base folder and Gruntfile 
  // You'll want to setup a fixture base folder and Gruntfile.js 
  // to ensure your Grunt'ing appropriately 
  base: 'path/to/a/gruntfile-base',
  gruntfile: 'path/to/a/Gruntfile.js'
 
  // Additional Grunt options can be specified here 
});
 
// Our actual benchmark 
module.exports = function(done) {
 
  // start the watch task 
  watchTask([function() {
 
    // After trigger found, run this sync function 
    // this will trigger the watch task 
    grunt.file.write('path/to/file.js', 'var test = false;');
 
  }, function() {
 
    // After the previous funciton has ran and another trigger hit... 
    // run this next sync function 
    grunt.file.delete('path/to/file.js');
 
  }], function(result) {
 
    // All done, do something more with the output result or finish up the benchmark 
    done();
 
  });
 
};

Test results will be saved to a file if a destination file is provided.

grunt.initConfig({
  benchmark: {
    singleTest: {
      src: ['benchmarks/fibonacci.js'],
      dest: 'results/fibonacci.csv'
    }
  }
});

Results can be saved as 'csv' or 'json'. The format is determined from the dest file extension or by setting the format option:

grunt.initConfig({
  benchmark: {
    singleTest: {
      src: ['benchmarks/fibonacci.js'],
      dest: 'results/fibonacci.txt',
      options : {
        format: 'csv'
      }
    }
  }
});

You can specify a truthy displayResults option inside your Grunt config to display the results using cli-table.

It will automatically pick up the dest property, so that must be set for this to work.

grunt.initConfig({
  benchmark: {
    options: {
      // This can also be set inside specific tests. 
      displayResults: true
    },
 
    singleTest: {
      src: ['benchmarks/fibonacci.js'],
      dest: 'results/fibonacci.csv'
    }
  }
});

The output will look something like:

Running "benchmark:fibonacci" (benchmark) task
 
Running suite Fibonacci [benchmarks/fibonacci.js]...
>> fibonacci x 13,386,628 ops/sec ±8.63% (74 runs sampled)
>> fibonacci_memoized x 30,509,658 ops/sec ±2.10% (89 runs sampled)
 
Results:
┌──────────────────────┬───────────────────────────────────────────┬───────┬─────────┬────────┬────────────────────┐
│ name                 │ date                                      │ error │ count   │ cycles │ hz                 │
├──────────────────────┼───────────────────────────────────────────┼───────┼─────────┼────────┼────────────────────┤
│ "fibonacci"          │ "Tue Apr 23 2013 21:25:49 GMT-0700 (PDT)" │       │ 906237  │ 4      │ 15154635.038364386 │
├──────────────────────┼───────────────────────────────────────────┼───────┼─────────┼────────┼────────────────────┤
│ "fibonacci_memoized" │ "Fri May 24 2013 19:52:02 GMT-0400 (EDT)" │       │ 1804104 │ 4      │ 31131880.83560733  │
├──────────────────────┼───────────────────────────────────────────┼───────┼─────────┼────────┼────────────────────┤
│ "fibonacci"          │ "Tue Apr 23 2013 22:10:55 GMT-0700 (PDT)" │       │ 910791  │ 4      │ 13386627.749339204 │
├──────────────────────┼───────────────────────────────────────────┼───────┼─────────┼────────┼────────────────────┤
│ "fibonacci_memoized" │ "Fri May 24 2013 19:52:11 GMT-0400 (EDT)" │       │ 1764921 │ 4      │ 30509657.596336514 │
└──────────────────────┴───────────────────────────────────────────┴───────┴─────────┴────────┴────────────────────┘

In lieu of a formal styleguide, take care to maintain the existing coding style. Lint and test your code using grunt.

  • 0.3.0 Add json output format (@creynders).
  • 0.2.0 Switched to benchmark.js. Huge thanks to @lazd!
  • 0.1.3 Ability to log dest to a csv file. Support Grunt@0.4.0rc7.
  • 0.1.2 Update to work with Grunt@0.4.0rc3.
  • 0.1.1 Fix require path
  • 0.1.0 Initial release

Copyright (c) 2014 Kyle Robinson Young
Licensed under the MIT license.