A lot of developers, especially ones who haven’t used Node.js in production, seem to believe that Node.js is faster than other interpreted languages such as PHP, Python, and Ruby.
These claims include:
- Node.js/V8 is x times faster than y language.
- The JavaScript reactor pattern will always handle concurrency better than any multi-threaded application.
I can certainly believe the first claim, yet I am unable to find any satisfactory benchmarks showing this. The second claim seems unfounded, or at least too absolute.
What are the characteristics of Node.JS and V8 that justify such claims?
12
Let’s have a look at the second claim.
The JavaScript reactor pattern will always handle concurrency better than any multi-threaded application.
To address this claim, I’m going to assume that concurrency in this context equates to scalability, since scalability is one of the primary motivations behind Node.JS. The distinction is subtle, but significant: there are many ways to get an application to scale better; concurrency is one way, but there are others. I’m also going to assume that NodeJS implements the reactor pattern, though I’m not going to describe it in great detail here.
By scalability, I mean “the ability to handle as many simultaneous requests as possible.” The way that node.js does this is by providing an event loop. The event loop accepts a request from the client, and then immediately hands it off to “some other mechanism” for processing. It then returns control to the caller. This asynchronous approach allows node.js to accept many requests in a given time frame, and differs from other, more synchronous strategies, where the caller must wait for the result before continuing. The amount of processing that the event loop demands is quite small, so it is ready (or almost ready) most of the time to accept new requests.
What happens to the requests once they are handed off? Well, for purposes of our discussion, that’s an implementation detail. A given request could be put into its own thread, or it could be queued on a thread containing others tasks. It could be handed to a Windows Service, or even sent to some other system for fulfillment.
The important thing to consider is the scalability that the event loop provides by making the acceptance of a request take as little time as possible. This doesn’t require a separate thread for each request; all you really need are two threads (one to run the event loop, and the other to process the requests).
Naturally, if you want your request to be fulfilled in some reasonable time frame, you must throw some horsepower at the implementation detail I mentioned earlier. There is no free lunch. But that’s a different practical concern than the scalability that arises by making sure that requests to a service are accepted rapidly, even though getting the results of those requests is deferred to some later time.
So when someone says something like “the reactor pattern handles concurrency better than multiple threads,” they’re being a bit imprecise. In a general way, they’re comparing asynchronous techniques to multi-threading (asynchronous programming does not necessarily require new threads). The way I would say it is:
By using an event loop, you can abstract the acceptance of a request away from the fulfillment of a request, thereby improving scalability without incurring the unnecessary overhead of additional threads.
Further Reading
A Quick Introduction to How Node.js Works
Understanding the node.js event loop
Reactor:
An Object Behavioral Pattern for
Demultiplexing and Dispatching Handles for Synchronous Events
I’ve implemented SCrypt libraries both on the clientside and serverside using js-scrypt and CryptSharp, and found the results shocking.
With chrome, execution time was over twice as fast as the .net implementation.
I never really paid much attention to the V8 engine until I observed these performance results, but now I have a very healthy respect for javascript executing on V8.
As for multi-threading, JS is single threaded and non-blocking, so code must be written async by default.
On a single processing core, javascript is basically forcing your code execution to be as processor efficient as possible, whereas serverside code can eat up threads and wait while other things are happening. So there are situations where there is work to be done and a core isn’t doing work.
For well written multithreaded code with many cores, and enough work to keep them all busy, js’s non-blocking single threaded code may not be able to compete. For example when simulating a universe on a super-computer.
However, thats not what Node.js is for. The vast majority of server code isn’t multi-thread optimized or even necessary, so non-blocking wins.