Javascript

Measuring Javascript Code Performance With Benchmark.js

javascript benchmark.js

Have you ever tested javascript code speed before. May be you write a custom code by yourself to calculate the elapsed to execute specific code block. In this tutorial we will look at a powerful javascript library to measure code performance.

 

The process of measuring code performance and speed is a Benchmark. In big languages such as Java or PHP to calculate the elapsed time that specific code could take to finish usually is to retrieve the start micro time before the script begin and the end micro time after the script has ended then subtract the end time from the start time and the resulting time is the elapsed time of this code.

// start time = get the start time

//  code block 

// end time = get the end time

// elapsed time = end time - start time

 

In javascript we can test specific code blocks and get other more details this with a very powerful javascript library called Benchmark.js . The library inspired from the jsPerf website.

 

To use benchmark.js just download the library from this link or install with npm and import it in your code as shown here:

<!DOCTYPE html>
<html>
<head>
	<title>Benchmark</title>
</head>
<body>
	
	<script src="js/lodash.js"></script>
	<script src="js/platform.js"></script>
	<script src="js/benchmark.js"></script>
</body>
</html>

As shown above in order for the benchmark to work you will need two other dependencies which are lodash and platform.js

 

Writing Benchmark Tests

The benchmark library has many helpful functions to return accurate timers and other related results:

Benchmark Suite

Benchmark.Suite(name [, options={}])

This class enables you to define a test suite then add test cases to the suite.

// create a new suite instance
var suite = new Benchmark.Suite;

The suite constructor can also take optional name and optional options object as follows:

// suite instance given the name and options
var suite = new Benchmark.Suite('app', {

     'onStart': onStart,

       // called between running benchmarks
     'onCycle': onCycle,

     ...

});

 

To add test cases to the suite use Suite.add(name, callback, [, options={}]) where:

  • name: name to identify the test case must describe the test case for example if you want to test the String.indexOf you can write the test case name as String#IndexOf
  • callback: The actual test function
  • [, options={}]: The options object

 

Examples

using function only:

suite.add(function() {
  
    !!'Hello World!'.match(/o/);
}); 

using name and function:

suite.add('String#match', function() {

    !!'Hello World!'.match(/o/);
});

using name and function and options:

suite.add('String#match', function() {

    !!'Hello World!'.match(/o/);
}, {
   'onStart': function() {},             // execute function on test start
   'onComplete': function() {}           // execute function on test complete
});

you can also emit the name and function and use options only:

suite.add({
  'name': 'String#match',
  'fn': function() {},
  'onStart': function() {},
  'onComplete': function() {}
});

 

After you add the test you can listen to events such as onCycle, onStart, onComplete, etc.

// called when the suite starts running
suite.on('start', function() { 
  
  console.log('bench started');
});

// called between running benchmarks
suite.on('cycle', function(e) { 
  
  console.log(String(event.target));
});

// called when aborted
suite.on('abort', function() {

  console.log('bench aborted');
});

// called when a test errors
suite.on('error', function() {
});

// called when reset
suite.on('reset', function() {
  
});

// called when the suite completed
suite.on('complete', function() {
  console.log('bench completed');
});

 

Finally run the test using Suite.run([options={}]) as follows:

// the normal call
suite.run();

or you can pass options as follows

suite.run({ 'async': true })

 

Let’s see an example in action using a real world example: user registration form

To simulate the process of test suites, imagine we have a user registration form and we need to submit this form to the server using ajax request, so the process will have the following components:

1. Validate the form (test case 1)

2. Send the ajax request (test case 2)

3. Show Success or Failure messages (test case 3)

 

For the purpose of this tutorial i will assume that the ajax request call a json file and return some status response, in real world you have to process the data into the server.



index.html

<!DOCTYPE html>
<html>
<head>
	<title>Benchmark</title>

	<style>
		.error {
			background-color: red;
		}

		.success {
			background-color: green;
		}
	</style>
</head>
<body>

	<form id="register-form" method="POST" onsubmit="return false;">
		<h3>Register</h3>

		<p id="status"></p>

		<p>
			<label>Username</label>
			<input type="text" name="username" id="username" />
		</p>
		<p>
			<label>Email</label>
			<input type="email" name="email" id="email" />
		</p>
		<p>
			<label>Password</label>
			<input type="password" name="password" id="password" />
		</p>
		<p>
			<label>Confirm Password</label>
			<input type="password" name="confirm_password" id="confirm_password" />
		</p>

		<p>
			<input type="submit" value="submit" onclick="submitForm()" />

			<input type="submit" value="Test case 1 (Validation)" onclick="testCase1()" />

			<input type="submit" value="Test case 2 (Ajax)" onclick="testCase2()" />

			<input type="submit" value="Test case 3 (Server response)" onclick="testCase3()" />

			<input type="submit" value="Test all process" onclick="testAll()" />
		</p>
	</form>
	
	<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
	<script src="js/platform.js"></script>
	<script src="js/benchmark.js"></script>
	<script src="js/form.js"></script>
	<script src="js/tests.js"></script>
</body>
</html>

As shown in the above we just created a simple form with main submit button and other buttons to validate the three test cases and the final button to test all cases.

Also we imported the required scripts the benchmark, along with lodash and platform.js. Next we imported two scripts which we will see next the first one of these is form.js will process the form validation and ajax and the second one is test.js will contain the test cases

 

create file js/form.js

var response = "waiting response...";

function validateForm()
{
	var errors = [];

	var username = document.getElementById("username").value;
	var email = document.getElementById("email").value;
	var password = document.getElementById("password").value;
	var confirm_password = document.getElementById("confirm_password").value;

	if(username == "") {

		errors.push('username required');
	}

	if(email == "") {

		errors.push('email required');
	}

	if(password == "") {

		errors.push('password required');
	}

	if(confirm_password == "") {

		errors.push('confirm password required');
	}

	if(password != "" && confirm_password != "" && password != confirm_password) {

		errors.push('password must be identical to confirm password');
	}

	return errors;
}

function send_ajax_request()
{
	var xhttp = new XMLHttpRequest();
	xhttp.responseType = 'json';
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {

        	response = this.response.message;

        	showResponse();

       }
    };

    xhttp.open("GET", "response.json", true);
    xhttp.send(); 
}

function submitForm()
{
	var errors = validateForm();

	if(errors.length) {

		document.getElementById("status").innerHTML = errors.join("<br/>");

		document.getElementById("status").setAttribute("class", "error");

		return false;
	}

	document.getElementById("status").innerHTML = "";

	send_ajax_request();
}

function showResponse()
{
	document.getElementById("status").setAttribute("class", "success");

    document.getElementById("status").innerHTML = response;
}

 

Create a mock file response.json to simulate server response

{"message": "user registered successfully!"}

Now if you click submit the form the validation messages appear, try to populate the form then submit again you will see a success response.

Now let’s setup the test cases so create a new file js/tests.js

var suite = new Benchmark.Suite;

function testCase1()
{
	suite.add('validation#test', function() {

		validateForm().length == 0;

	});

	suite.run({'async': true});
}

function testCase2()
{
	suite.add('ajax#test', function() {

        // be careful when running this test as it will make a heavy load on the browser
		send_ajax_request();

	});

	suite.run({'async': true});
}

function testCase3()
{
	suite.add('response#test', function() {

		showResponse();

	});

	suite.run({'async': true});
}

function testAll()
{
	suite.add('validation#test', function() {

		submitForm();

	});

	suite.run({'async': true});
}

suite.on("start", function() {
	console.log("Test suite started");
});

suite.on("cycle", function(event) {
	console.log(String(event.target));
});

suite.on("complete", function() {

	console.log("Test completed");

	console.log('Fastest is ' + this.filter('fastest').map('name'));
});

As shown in the above code first we create a new instance from Benchmark.Suite then we add four for each test case as shown:

function testCase1()
{
        // set test case name and function
	suite.add('validation#test', function() {

		validateForm().length == 0;

	});

	suite.run({'async': true});
}

 

After we add the test cases we call suite.run() which run our test cases, then we set some listeners such as start to mark when the test start and cycle to get sequential loop of all tests along with their time, finally we filtered the tests using the filter method to get the fastest test:

suite.on("complete", function() {

	console.log("Test completed");

	console.log('Fastest is ' + this.filter('fastest').map('name'));
});

 

Now if click on any of the test cases and then toggle the developer options > console in chrome and wait a couple of seconds you will see something like this:

Test suite started
validation#test x 1,538,841 ops/sec ±91.62% (9 runs sampled)
Test completed
Fastest is validation#test

This log shows each test name and number of operations per second

5 1 vote
Article Rating

What's your reaction?

Excited
0
Happy
1
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments