производительность node.js с zeromq против Python vs.Java

Я написал простой тест эхо-запроса / ответа для zeromq с использованием node.js, Python и Java. Код выполняет цикл из 100 000 запросов. Платформа представляет собой 5-летний MacBook Pro с 2 ядрами и 3G ОЗУ под управлением Snow Leopard.

node.js постоянно на порядок медленнее, чем две другие платформы.

Java: реальный 0 мин. 18,823 с. пользователь 0m2.735s sys 0m6.042s

Python: реальный 0 мин. 18,600 с пользователь 0m2.656s sys 0m5.857s

node.js: реальные 3 мин. 19,034 с. пользователь 2m43.460s sys 0m24.668s

Интересно, что в Python и Java процессы клиента и сервера используют примерно половину ЦП. Клиент для node.js использует почти полный ЦП, а сервер использует около 30% ЦП. Клиентский процесс также имеет огромное количество ошибок страниц, что наводит меня на мысль, что это проблема памяти. Кроме того, при 10К запросах узел работает всего в 3 раза медленнее; он определенно замедляется тем больше, чем дольше работает.

Вот клиентский код (обратите внимание, что строка process.exit () тоже не работает, поэтому я включил внутренний таймер в дополнение к использованию команды 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);
});

Код сервера:

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);
                });
            }
);

Для сравнения, код клиента и сервера 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!")

И код клиента и сервера 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();
    }
}

Мне бы понравился узел, но с большой разницей в размере кода, простота и производительность, мне было бы трудно убедить себя на этом этапе.

Итак, видел ли кто-нибудь подобное раньше, или я сделал что-то глупое в коде?

29
задан Scott A 24 August 2012 в 14:32
поделиться

3 ответа

Это была проблема с привязками zeroMQ узла. С тех пор я не знаю, но это исправлено, и вы получите те же результаты, что и с другими языками.

9
ответ дан 28 November 2019 в 01:47
поделиться

Любое тестирование производительности с использованием сокетов REQ / REP будет искажено из-за циклических отключений и задержек потоков. По сути, вы просыпаете весь стек, весь путь вниз и вверх, для каждого сообщения. Это не очень полезно в качестве показателя, потому что случаи REQ / REP никогда не бывают высокими (не могут быть). Существует два теста на лучшую производительность:

  • Отправка множества сообщений разного размера от 1 байта до 1 КБ, посмотрите, сколько вы можете отправить, например, 10 секунд. Это дает вам базовую пропускную способность. Это говорит вам, насколько эффективен стек.
  • Измерение сквозной задержки, но потока сообщений; То есть вставьте отметку времени в каждое сообщение и посмотрите, какое отклонение получателя. Это говорит вам, есть ли в стеке дрожание, например из-за сбора мусора.
2
ответ дан 28 November 2019 в 01:47
поделиться

Вы используете стороннюю привязку C ++. Насколько я понимаю, пересечение между "js-land" в v8 и привязками к v8, написанными на "c ++ land", очень дорого. Если вы заметили, некоторые популярные привязки базы данных для узла реализованы полностью в JS (хотя, отчасти я уверен, потому что люди не хотят что-то компилировать, но и потому, что это имеет потенциал быть очень быстрым).

Если я правильно помню, когда Райан Даль писал объекты Buffer для узла, он заметил, что они на самом деле были намного быстрее, если он реализовал их в основном в JS, а не в C ++. Он закончил тем, что написал то, что должен был в C ++ , и сделал все остальное в чистом javascript .

Итак, я предполагаю, что часть проблемы с производительностью здесь связана с тем, что этот конкретный модуль является связыванием c ++.

Оценка производительности узла на основе стороннего модуля не является хорошим средством для определения его скорости или качества. Вы могли бы сделать намного лучше для сравнения собственного интерфейса TCP узла.

17
ответ дан 28 November 2019 в 01:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: