I am benchmarking a Java UDP client that continuously sends datagrams with a payload of 100 bytes as fast as it can. It was implemented using java.nio.*
. Tests show that it's able to achieve a steady throughput of 220k datagrams per second. I am not testing with a server; the client just sends the datagrams to some unused port on localhost.
I decided to run the same test in Node.js to compare both technologies and it was surprisingly sad to see that Node.js performed 10 times slower than Java. Let me walk you through my code.
First, I create a UDP socket using Node.js's dgram module:
var client = require('dgram').createSocket("udp4");
Then I create a function that sends a datagram using that socket:
function sendOne() {
client.send(message, 0, message.length, SERVER_PORT, SERVER_ADDRESS, onSend);
}
The variable message
is a buffer created from a string with a hundred characters when the application starts:
var message = new Buffer(/* string with 100 chars */);
The function onSend
just increments a variable that holds how many datagrams were sent so far. Next I have a function that constantly calls sendOne()
using setImmediate()
:
function sendForever() {
sendOne();
setImmediate(sendForever);
}
Initially I tried to use process.nextTick(sendForever)
but I found out that it always puts itself at the tip of the event queue, even before IO events, as the docs says:
It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.
This prevents the send IO events from ever happening, as nextTick
is constantly putting sendForever
at the tip of the queue at every tick. The queue grows with unread IO events until it makes Node.js crash:
fish: Job 1, 'node client' terminated by signal SIGSEGV (Address boundary error)
On the other hand, setImmediate
fires after I/O events callbacks, so that's why I'm using it.
I also create a timer that once every 1 second prints to the console how many datagrams were sent in the last second:
setInterval(printStats, 1000);
And finally I start sending:
sendForever();
Running on the same machine as the Java tests ran, Node.js achieved a steady throughput of 21k datagrams per second, ten times slower than Java.
My first guess was to put two sendOne
's for every tick to see if it would double the throughput:
function sendForever() {
send();
send(); // second send
setImmediate(sendForever);
}
But it didn't change the throughput whatsoever.
I have a repository available on GitHub with the complete code:
https://github.com/luciopaiva/udp-perf-js
Simply clone it to your machine, cd
into the folder and run:
node client
I want to open a discussion about how this test could be improved in Node.js and if there's some way we can increase Node.js's throughput. Any ideas?
P.S.: for those interested, here is the Java part.
P.S.
section does not work. Could you please update it ? – Jan Grz