After having somewhat of a heated Facebook conversation with a friend about the Ups and Downs of various web frameworks, I was inspired to benchmark a simple “Hello World” to compare the speed and resource usage of Node.js and the Play! Framework (using Java).
Being mostly Perl guys ourselves, my go-to tool for web wizardry is generally the awesome Catalyst MVC framework, can’t say the same about my friend.
All the tests below were completed on my Laptop – Intel Core i5 M480 CPU @ 2.67GHz (2 cores, 4 threads) with 8 GB RAM running Ubuntu 13.10.
I’m comparing node.js v0.10.15, Play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_51) and using ApacheBench 2.3 in the following benchmarks, monitoring system resource usage using Dstat 0.7.2.
I know it’s not a very exhaustive set of benchmark tests, but the ApacheBench command I ran made 100,000 requests with a concurrency of 1,000.
~$ ab -r -n 100000 -c 1000 <url>
Ok, now for the fun stuff.
The Play! Framework
After creating the basic application template made by running
~$ play new hello
I simply changed the app/controllers/Application.java to contain the following:
And the results?
Concurrency Level: 1000
Not too shabby if you ask me.
11,410.73 requests per second, and 8.764 seconds to complete the test.
So obviously node.js can do better, right?
This one was much simpler to get up and runnning. My hello.js is as follows:
var sys = require('sys'),
Show me the money!
Uh oh, this isn’t quite what we’d both hoped for from the fabled node.js…
Concurrency Level: 1000
So node.js handled 5,565.76 requests a second, and took a whopping (comparatively speaking) 17.967 seconds to complete the benchmark. Like I said, not the stunning, life-changing, backend-rewrite-worthy results we’d been hoping for.
Then came the brain-fart moment.
Node.js was only using one core whereas according to this bit of the Play! Framework documentation “[The default Play configuration] instructs Akka to create one thread per available processor, with a maximum of 24 threads in the pool.”
Well, that’s hardly a fair test, is it?
Now for the real test – C’mon node!
After a few tweaks to the original node script, and after reading about the Cluster module, I was left with the following:
var cluster = require('cluster');
Surely now node.js can live up to those expectations of ours, right?
Concurrency Level: 1000
Wahoo! This time it only took node 9.502 seconds to get through all of those requests, and it managed to handle a fairly decent 10524.19/second.
Still a teeny bit shy of the ever-impressive Play! Framework, but now lets take a look at the system load all this testing generated.
They’re both quick, but how about their system resource usage?
The first graph here shows CPU usage throughout the testing. The Play test came first, followed by the basic node.js test and lastly the clustered node.js server test.
Hold up a second – looks like I ran 6 tests, doesn’t it?
Ok, so I did. Play! had to compile the application the first time I ran it, so I chose to run three tests twice, and then share the results from the best scoring test. IMHO it seemed fair enough at the time, if you don’t feel that way let me know and I’m happy to run them again.
As you can see, and somewhat surprisingly, it appears that once the Java has been compiled the CPU usage is much the same as the clustered node.js example.
In the same fashion as the first chart, this is the Play Framework test followed by the basic node.js example and finally the clustered node.js server. As you can see, and this time not so surprisingly Java gobbles up as much RAM as it can which node.js gets the task done with minimal extra usage.
So the final verdict?
If memory usage is of great concern to you, it would seem that node.js is the way to go. If not, and you do perhaps prefer Java development, then Play may be the way to go (though from what I’ve seen so far the documentation isn’t what it could be.
If you like being given the a basic MVC architechted application framework with which to build your application, then there’s another brownie point for Play. If RYO is more your style, then perhaps node.js is for you.
And before you go and hate on me…
I know that this is hardly a real-world use case. With most applications there are at least some elements of database operations, caching, front-end proxies (whether these be via varnish/nginx/noSQL stores/a CDN or other means), as well as much more complex logic and other moving parts.
However, as a simple test of the minimum overhead of these two tools, and a way to see the bog-standard amount of requests you should be able to push out of them I feel this is fairly fitting.
Also, if you look at this as somewhat of a guide as to how easy it is to get set up with node.js or Play, you’ll notice that Play comes with a fairly well tried and tested MVC feel about it, and gives a bit more of a predefined structure to your application – granted you may or may not want this.
And one last thing before I go: yes, this was heavily inspired by Maciej Zgadzaj’s post here.