Skip to content
This repository

Docs 

trevnorris edited this page · 1 revision

Pages 2

Clone this wiki locally

ToC

Properties

Timer.delay( n )

Set delay between synchronous benchmark execution.

Synchronous benchmarks run with a default 300ms between each to allow for garbage collection or any other internals to cleanup, as to not affect the next benchmark. This can be changed by passing a positive number. All non-numeric values or values less than 0 are ignored.

Timer.maxNameLength()

Returns the length of the longest test name.

When many benchmarks are loaded, it's helpful to know the length of the longest name so formating of the output can be improved. This value can only be known after all the benchmarks have been initialized. So if this is used it is helpful to create all benchmarks early as possible.

Timer.parse( argv )

Parse command line parameters.

Simple benchmark customization can be achieved by passing parameters. These parameters can be automatically parsed by passing process.argv. Any custom set of parameters are accepted, on the condition they all start with --. Numbers of type int (e.g. 5), float (e.g. 1e3) and hex (e.g. 0xf) are automatically parsed from strings to their respective values. The returned object contains values of the following pattern:

$ node test.js --boolean --number 0xf --string arg --array arg0 1e3
Timer.parse(process.argv);

// output:
// {
//   boolean: true,
//   number: 15,
//   string: 'arg',
//   array: ['arg0', 1000]
// }

The --exec parameter is automatically used by Timer to determine what synchronous tests should be processed. This is useful when you want to run external tools against the benchmark. For example:

$ node  --trace-opt --trace-deopt --trace-inlining --code-comments \
        simple-test.js --exec test1 --iter 1e6 --noop

Sync

Synchronous tests have been specifically designed to reduce performance impact on benchmarks. All passed functions are run only once. Because of this it is up to the user to determine how many operations are performed within the function (e.g. iterate over a method n times within a for loop). This method of testing is also advantageous because it allows to user to easily manipulate the call quickly and see what types of regressions can occur. For example:

var Timer = require('../lib/node-timer');

Timer('regression0', function regression0() {
  var arr = [1];
  for (var i = 0; i < 1e6; i++) {
    simpleExternal(arr[0]);
    if (i % 1e5 === 0)
      simpleExternal(arr[0], arr[1]);
  }
});

function simpleExternal(arg0, arg1, arg2) { }

Produces some of the following:

$ node --trace-deopt --code-comments test-regression.js

**** DEOPT: regression0 at bailout #7, address 0x0, frame size 48
            ;;; Deferred code @88: stack-check.
[deoptimizing: begin 0x8f5a6f91071 regression0 @7]
  translating regression0 => node=38, height=24
    0x7fff723d5148: [top + 56] <- 0x8f5a6f910d9 ; rcx 0x8f5a6f910d9 <a initSync>
    0x7fff723d5140: [top + 48] <- 0x603b3b75751 ; caller's pc
    0x7fff723d5138: [top + 40] <- 0x7fff723d5170 ; caller's fp
    0x7fff723d5130: [top + 32] <- 0x8f5a6f7aa31; context
    0x7fff723d5128: [top + 24] <- 0x8f5a6f91071; function
    0x7fff723d5120: [top + 16] <- 0x8f5a6f92051 ; rbx 0x8f5a6f92051 <JS Array[1]>
    0x7fff723d5118: [top + 8] <- 11779 ; rax (smi)
...

Timer( name, fn )

Timer will return a new instance and be pushed to the benchmark queue.

var iter = 1e6;

Timer('simple-loop', function() {
  for (var i = 0; i < iter; i++)
    // perform something
});

timer.oncomplete( fn [, args ])

By default a synchronous benchmark will print name: time µs. This can be overridden using oncomplete():

var timer = Timer('abc', function() { });

timer.oncomplete(function(name, hrtime, args) {
  // custom oncomplete message
  // hrtime is in process.hrtime() format
}, aa);

The reasoning for allowing arguments to be passed to oncomplete is that an external template may be used that doesn't reside within the same scope:

var template = require('./external-templates');

Timer('name', function() { })
  .oncomplete(template.oncomplete, args);

Async

Timer( name [, delay, term, kill ])

Asynchronous timers have two types. Simple and interval. The later having three variants. These are explained below in detail.

Timer( name )

A simple async benchmark is created when only name is passed. While the internal clock does initiate when the timer is created, it's more likely it will need a later start() point in the script.

var async = Timer('first');

// setup more stuff

async.start();

// begin test

The only difference between these and interval timers are that they aren't setup using setInterval and will not run oninterval callbacks.

Timer( name, delay [, term, kill ])

Every delay ms after start() a function will run. Timer supplies a default that will output the number of inc() that have occurred since start() or the last oninterval. This can be overridden as explained below. The timer can also run a function onend, but does not supply one by default.

If term is passed then the timer will cancel it's own setInterval after term iterations and run end. Though the script itself will not end. This is useful if you wish to track multiple asynchronous timers in succession.

var async0 = Timer('async0', 1000, 3);
var async1 = Timer('async1', 1000);
var a0si;

async0.onstart(function() {
  var a0si = setInterval(function() {
    async0.inc();
  }, 20);
});

async0.onend(function() {
  clearInterval(a0si);
  async1.start();
});

async1.onstart(function() {
  setInterval(function() {
    async1.inc();
  }, 10);
});

async0.start();

// output:
// async0: 49.39/sec
// async0: 49.45/sec
// async0: 49.01/sec
// async1: 97.95/sec
// async1: 98.02/sec
// ...

If kill is passed as true then process.exit() will be called when the benchmark is complete.

timer.cancel()

Cancels the setInterval that calls oninterval for the benchmark. This can be re-initialized with start() again, but remember that the internal clock will be reset.

timer.end([ args ])

Terminate the benchmark. If args are passed, note they will override any that were passed to onend().

timer.inc([ n ])

Increment the internal counter by 1 or by n. To have as little performance impact as possible all that's checked is typeof n == 'number'. You will observe deoptimizations in your code if the internal counter exceeds Smi size.

timer.start()

Setup the setInterval. If a user has passed a function to onstart() then it will be run before the internal clock begins.

timer.noop()

Remove all events and increments. The test will occur with the least possible interference. Useful when generating output from valgrind or similar tools. Use case usually looks like the following:

var params = Timer.parse(process.argv);
var bench = Timer('async');

// setup everything
bench.onstart(function() {});
bench.onend(function() { });

// check if user wants noop
if (params.noop)
  bench.noop();

timer.onend( fn [, args ])

Runs fn with name, time, counter and args, if passed, before the final sequence. Note that if the benchmark was initiated with kill then the process will still exit.

timer.oninterval( fn [, args ])

Runs fn with name, time, counter and args, if passed, between every interval. This will not prevent the internal counter from resetting at interval.

timer.onstart( fn [, args ])

Run fn at start before the internal counter has been reset.

Notes

Good benchmarks can be difficult to write. If those who are new to, and even to those who consider themselves experienced with, writing benchmarks for JavaScript I'd recommend reading μbenchmarks fairy tale.

Something went wrong with that request. Please try again.