About Me

Curriculum Vitae

A brief list of my current skill set

Bloggybits

Upgrading to PostgreSQL 9.2 on CentOS 5
Tuesday, 25th September 2012, 14:52

It's easy as PI!

Fare Ye Well Work Email You Have Served Me Well
Monday, 17th September 2012, 14:36

Cause of death too much spam

Forest Racer - A HTML5 Game in Under 13K
Tuesday, 11th September 2012, 20:46

Including all assets, but only when zipped

Entering a 13k HTML5 Game Competition
Tuesday, 4th September 2012, 16:31

I'm so tempted to have a go at this

Faster Loops and Faster Iterations in Node.js and V8
Wednesday, 29th August 2012, 13:16

Is Object.keys faster than For...In?

And the Fastworks.js framework is Born!
Wednesday, 22nd August 2012, 16:23

Well I'm excited, even if you aren't

Libxmljs Update on CentOS 3.8 throws an SELinux Wobbley Fit
Monday, 20th August 2012, 15:40

The right way to fix this sort of issue

TV Land Doesn't Understand Technology
Friday, 17th August 2012, 17:09

Or maybe it does and thinks we don't?

Yet More Benchmarking - Function Chains vs Object Chains
Wednesday, 15th August 2012, 13:34

Working towards a faster Node.js framework

More Benchmarking in Node.js and V8
Tuesday, 14th August 2012, 12:19

Working out the fastest way to route

MinnaHTML.js Benchmarking for Speed in Node.js
Monday, 13th August 2012, 17:55

Don't believe it, test it

Playing Around With HTML5's Canvas
Friday, 10th August 2012, 16:19

Speccy loading screens in a browser!

Scripting in Node.js AKA How to Watch for Olympic Tickets Using a Script
Tuesday, 7th August 2012, 23:37

Let Node refresh the webpage so you don't have to!

A Javascript Confirm/Alert Replacement jQuery Plugin
Monday, 6th August 2012, 23:06

Much prettier than the horrible alert and confirm dialogs

A Few Node.js Essential Modules
Friday, 3rd August 2012, 14:40

As essential as they can be anyway

Projects and Sillyness

MAME Cabinet Diary

How I built my own arcade cabinet

Loading Screen Simulator

I don't miss the ZX Spectrum, I still use it!

The Little Guy Chat Room

It's a Pitfall inspired chat room

GPMad MP3

A fully featured MP3 player what I wrote

GP Space Invaders

My first little emulator

GP32 Development Page

Some info and links about this cute little handheld

Disney Nasties

Uncensored images, you must be 18 to view them

Minna's World

The cuttest fluffy puppy in the world

Diary of a Hamster

Learn about how hamsters think, first hand

Utilities

Time Calculator

A simple little online utility for working out how many hours to bill a client

A Few Links

Yet More Benchmarking - Function Chains vs Object Chains
Wednesday, 15th August 2012, 13:34

Yesterday, I spent some time benchmarking different ways to deal with routing for my forthcoming Connect framework replacement for Node.js. Today I'm going to look at the stack side of things, where our matched route will be sent. I can think of a few ways this could be done, so time to put them to the test.

Iterating An Array

The most simple one this, our stack function runs through an array and calls every function in it with the request/response fields. I'm not expecting this to be the fastest method, but it will probably be quite close. Here is our code:

var runtimes = 1000000;

var stack = [
doNothing,
doNothing,
doNothing,
doNothing,
doNothing,
doNothing,
doNothing,
doNothing
];

var dtStart = new Date();

for (var runcount = 0; runcount < runtimes; runcount++)
callStackArray();

var dtEnd = new Date();

console.log("Runs: " + runtimes + " Total Time: " + (dtEnd - dtStart) + "ms, Avg Time: " + (dtEnd - dtStart) / runtimes);

function doNothing()
{
var x = Math.random();

return true;
}

function callStackArray()
{
var response = {};
var request = {};

for (var u = 0; u < stack.length; u++)
{
if (!stack[u](request, response))
break;
}
}

And our results are as follows:

Runs: 1000000 Total Time: 208ms, Avg Time: 0.000208

The immediate downside of this method, is every function in the stack needs to be blocking, async calls inside the stack are right out. This might not be a bad thing, as whilst perhaps it rules out middleware that does things like serve static objects, maybe that is best left to routing.

At the same time, form data may be a ruiner here, perhaps you don't want progress through your stack to continue until an entire file has been uploaded, perhaps you do so you can return a progress status? Maybe choice is the watch word.

Function Chaining

This method definitely allows for an async stack, leaving it entirely up to one middleware to call the next. In essence, this is how Connect does it, although there seems to be an awful lot of code involved in the process. For this to work we have to use a bit more code to build our function stack:

var funcstack = [
doNothing
];

for (var p = 0; p < 8; p++)
funcstack.unshift(getNothing(funcstack[0]));

function doNothing()
{
}

function getNothing(next)
{
return function(request, response)
{
var x = Math.random();

next(request, response);
}
}

We start off with our doNothing() function from before, and then we add a list of functions to our stack, created by the getNothing() function. Because we have to pass it the function it should call upon completion, we have to build the array in reverse order.

Our testing function becomes simply this:

function callStackFuncChain()
{
funcstack[0]();
}

Since all it has to do is call the first function in the stack, the remaining ones are just chained. And the results are as follows:

Runs: 1000000 Total Time: 141ms, Avg Time: 0.000141

Significantly faster than running through an array. I did a little further testing, making the doNothing() function return something, and that slowed things down a bit, but it was still faster than using an array:

Runs: 1000000 Total Time: 176ms, Avg Time: 0.000176

Object Chaining

If this proves as fast as function chaining, this would be very handy from a coding perspective. Whilst there is no reason why you couldn't create stacks dynamically (though you'd never want to do it often hopefully) with the function chaining method, if we could use chaining objects then we could do things like removing and adding an entry into a stack on the fly.

Log some data from a high volume site and then remove yourself from the stack so your impact post-log is zero. Maybe throttle connections at the start to give the database server time to get into full flow, then disappear. But let's stop thinking about it in case the performance is poor.

Our object and creation of our stack now look like this:

function StackFunction(previous, next)
{
this.previous = previous;
this.next = next;
}

StackFunction.prototype.doNothingIsh = function(request, response)
{
var x = Math.random();

this.next.doNothingIsh(request, response);
}

StackFunction.prototype.doNothingAfterIsh = function(request, response)
{
var x = Math.random();
}

var objstack = [];

for (var p = 0; p < 8; p++)
objstack.unshift(new StackFunction(null, objstack[0]));
for (p = 1; p < objstack.length; p++)
objstack[p].previous = objstack[p - 1];
objstack[7].doNothingIsh = objstack[7].doNothingAfterIsh;

Note that here we do something a bit odd. Our stack function object is set up like a linked list, and we set the doNothingIsh function for our last entry in the list to one that doesn't call another.

Our test function just looks like this:

function callStackObjChain()
{
var response = {};
var request = {};

objstack[0].doNothingIsh(request, response);
}

And our results are surprisingly good:

Runs: 1000000 Total Time: 115ms, Avg Time: 0.000115

Object chaining works really quickly! Faster than function chaining! Even adding a doNothing() call inside doNothingAfterIsh() has little to no effect. Further more, I made another change, inside doNothingIsh I went with:

if (this.next)
this.next.doNothingIsh(request, response);

The hit is small as you can see, it still is more efficient than function chaining:

Runs: 1000000 Total Time: 131ms, Avg Time: 0.000131

So perhaps we'll be quite anally retentive about it all and stick with the previous method.

Conclusions

V8 appears to be very well optimised for object use, so we can go that route safe in the knowledge we have the fastest way of doing things, for now anyway.

The start of my framework now looks to be quite well set in stone. Simple string match or regexp match based routing, the choice being the users, which send requests via a stack to a destination function.

I'm probably going to scale back the benchmarking for a short while, because it is quite time consuming and much of what is to come can be written and then optimised later.

Comments

Add Your Own Comment