J'ai écrit un test simple de demande/réponse d'écho pour zeromq en utilisant node.js, Python et Java. Le code exécute une boucle de 100K requêtes. La plateforme est un MacBook Pro de 5 ans avec 2 cœurs et 3G de RAM fonctionnant sous Snow Leopard.
node.js est systématiquement plus lent d'un ordre de grandeur que les deux autres plateformes.
Java : real 0m18.823s user 0m2.735s sys 0m6.042s
Python : real 0m18.600s user 0m2.656s sys 0m5.857s
node.js : real 3m19.034s user 2m43.460s sys 0m24.668s
Il est intéressant de noter qu'avec Python et Java, les processus client et serveur utilisent tous deux environ la moitié d'une unité centrale. Le client pour node.js utilise à peu près un CPU complet et le serveur utilise environ 30% d'un CPU. Le processus client présente également un nombre énorme de défauts de page, ce qui me laisse penser qu'il s'agit d'un problème de mémoire. De plus, à 10 000 demandes, node n'est que 3 fois plus lent ; il ralentit définitivement plus il s'exécute longtemps.
Voici le code client (notez que la ligne process.exit() ne fonctionne pas non plus, c'est pourquoi j'ai inclus un timer interne en plus de l'utilisation de la commande time) :
var zeromq = require("zeromq");
var counter = 0;
var startTime = new Date();
var maxnum = 10000;
var socket = zeromq.createSocket('req');
socket.connect("tcp://127.0.0.1:5502");
console.log("Connected to port 5502.");
function moo()
{
process.nextTick(function(){
socket.send('Hello');
if (counter < maxnum)
{
moo();
}
});
}
moo();
socket.on('message',
function(data)
{
if (counter % 1000 == 0)
{
console.log(data.toString('utf8'), counter);
}
if (counter >= maxnum)
{
var endTime = new Date();
console.log("Time: ", startTime, endTime);
console.log("ms : ", endTime - startTime);
process.exit(0);
}
//console.log("Received: " + data);
counter += 1;
}
);
socket.on('error', function(error) {
console.log("Error: "+error);
});
Code du serveur :
var zeromq = require("zeromq");
var socket = zeromq.createSocket('rep');
socket.bind("tcp://127.0.0.1:5502",
function(err)
{
if (err) throw err;
console.log("Bound to port 5502.");
socket.on('message', function(envelope, blank, data)
{
socket.send(envelope.toString('utf8') + " Blancmange!");
});
socket.on('error', function(err) {
console.log("Error: "+err);
});
}
);
Pour comparaison, le code du client et du serveur Python :
import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://127.0.0.1:5502")
for counter in range(0, 100001):
socket.send("Hello")
message = socket.recv()
if counter % 1000 == 0:
print message, counter
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://127.0.0.1:5502")
print "Bound to port 5502."
while True:
message = socket.recv()
socket.send(message + " Blancmange!")
Et le code du client et du serveur Java :
package com.moo.test;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
public class TestClient
{
public static void main (String[] args)
{
Context context = ZMQ.context(1);
Socket requester = context.socket(ZMQ.REQ);
requester.connect("tcp://127.0.0.1:5502");
System.out.println("Connected to port 5502.");
for (int counter = 0; counter < 100001; counter++)
{
if (!requester.send("Hello".getBytes(), 0))
{
throw new RuntimeException("Error on send.");
}
byte[] reply = requester.recv(0);
if (reply == null)
{
throw new RuntimeException("Error on receive.");
}
if (counter % 1000 == 0)
{
String replyValue = new String(reply);
System.out.println((new String(reply)) + " " + counter);
}
}
requester.close();
context.term();
}
}
package com.moo.test;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
public class TestServer
{
public static void main (String[] args) {
Context context = ZMQ.context(1);
Socket socket = context.socket(ZMQ.REP);
socket.bind("tcp://127.0.0.1:5502");
System.out.println("Bound to port 5502.");
while (!Thread.currentThread().isInterrupted())
{
byte[] request = socket.recv(0);
if (request == null)
{
throw new RuntimeException("Error on receive.");
}
if (!socket.send(" Blancmange!".getBytes(), 0))
{
throw new RuntimeException("Error on send.");
}
}
socket.close();
context.term();
}
}
J'aimerais aimer node, mais avec la grande différence de taille de code, de simplicité et de performance, j'aurais du mal à me convaincre à ce stade.
Quelqu'un a-t-il déjà vu un tel comportement ou ai-je fait quelque chose de stupide dans le code ?