Почему шаблон ZeroMQ ROUTER-DEALER имеет большую задержку? - PullRequest
2 голосов
/ 28 июня 2019

Использование libzmq 4.2.5 для centos 7. Получение очень высокой задержки при отправке сообщений от DEALER до ROUTER и даже от ROUTER до DEALER. Поэтому я написал простую клиент-серверную программу, использующую tcp, и отправил между ними сообщения только для сравнения. Tcp кажется быстрым.

Отправка одного байта от DEALER до ROUTER, zmq занимает 900 микросекунд.

Отправка одного байта с клиента на сервер, tcp занимает 150 мкс.

Что я делаю не так. Я думал, что zmq будет по крайней мере так же быстро, как TCP. Могу ли я сделать какие-либо настройки, чтобы сделать zmq быстрее?

Обновление

router.cpp

#include <zmq.hpp>
    struct data
    {
    char one[21];
    unsigned long two;
   };
data * pdata;
std::size_t counter=0;

 int main()
{
   zmq::context_t context(1);
   zmq::socket_t Device(context,ZMQ_ROUTER);

   int iHighWaterMark=0;

  Device.setsockopt(ZMQ_SNDHWM,&iHighWaterMark,sizeof(int));
  Device.setsockopt(ZMQ_RCVHWM,&iHighWaterMark,sizeof(int));

  Device.bind("tcp://0.0.0.0:5555");

  pdata=new data[10000];

 struct timespec ts_dtime;
 unsigned long sec;

  zmq::message_t message;

  zmq::pollitem_t arrPollItems[]={{Device, 0, ZMQ_POLLIN, 0},{NULL, 
                                                            0, ZMQ_POLLIN, 0}};

    while(counter < 10000)
      {
        try
      {
        int iAssert = zmq::poll(arrPollItems, 1, -1);
        if (iAssert <= 0)
          {
             if (-1 == iAssert)
          {
             printf("zmq_poll failed errno: %d error:%s", errno, 
                                 zmq_strerror(errno));
          }
            continue;
           }

          if (arrPollItems[0].revents == ZMQ_POLLIN)
           {
             while(true)
            {
               if(! Device.recv(&message,ZMQ_DONTWAIT))
                    break;

               Device.recv(&message);

                strncpy(pdata[counter].one, 
                                      (char*)message.data(),message.size());
               clock_gettime(CLOCK_REALTIME, &ts_dtime);
              pdata[counter].two = (ts_dtime.tv_sec*1e9)+ 
                                                              ts_dtime.tv_nsec;
              ++counter;
            }

           }
      }
          catch(...)
        {

         }

          }

         for(int i=0;i<counter;++i)
         printf("%d %s %lu\n",i+1,pdata[i].one,pdata[i].two);

         return 0;
        }

dealer.cpp

#include <zmq.hpp>
#include<unistd.h>

int main()
{
  zmq::context_t context(1);
  zmq::socket_t Device(context,ZMQ_DEALER);

  int iHighWaterMark=0;

  Device.setsockopt(ZMQ_SNDHWM,&iHighWaterMark,sizeof(int));
  Device.setsockopt(ZMQ_RCVHWM,&iHighWaterMark,sizeof(int));
  Device.setsockopt(ZMQ_IDENTITY,"TEST",4);

   Device.connect("tcp://0.0.0.0:5555");

    usleep(100000);

   struct timespec ts_dtime;
   unsigned long sec;

   for(std::size_t i=0;i<10000;++i)
    {
      clock_gettime(CLOCK_REALTIME, &ts_dtime);
      sec=(ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec;
      zmq::message_t message(21);
      sprintf((char *)message.data(),"%lu",sec);
      Device.send(message);
     usleep(500);
    }

  return 0;
 }

обновление 2:

router.cpp

#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
    const char *bind_to;
    int roundtrip_count;
    size_t message_size;

    int rc;
    int i;


    if (argc != 4) {
        printf ("usage: local_lat <bind-to> <message-size> "
                "<roundtrip-count>\n");
        return 1;
    }
    bind_to = argv[1];
    message_size = atoi (argv[2]);
    roundtrip_count = atoi (argv[3]);

    zmq::context_t ctx(1);
  zmq::socket_t s(ctx,ZMQ_ROUTER);

  zmq::message_t msg,id;

    int iHighWaterMark=0;
    s.setsockopt(ZMQ_SNDHWM , &iHighWaterMark,
                         sizeof (int));
    s.setsockopt(ZMQ_RCVHWM , &iHighWaterMark,
                                              sizeof (int));
    s.bind( bind_to);
    struct timespec ts_dtime;
    unsigned long sec;
for (i = 0; i != roundtrip_count; i++) {
      rc =s.recv(&id);
        if (rc < 0) {
            printf ("error in zmq_recvmsg: %s\n", zmq_strerror (errno));
            return -1;
        }

        rc = s.recv(&msg, 0);
        if (rc < 0) {
            printf ("error in zmq_recvmsg: %s\n", zmq_strerror (errno));
            return -1;
        }

        clock_gettime(CLOCK_REALTIME, &ts_dtime);
        sec=((ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec);
        printf("%.*s %lu\n",20,(char *)msg.data(),sec);
}




    s.close();



    return 0;
}

dealer.cpp

#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
    const char *connect_to;
    int roundtrip_count;
    size_t message_size;

    int rc;
    int i;

    void *watch;
    unsigned long elapsed;
    double latency;

    if (argc != 4) {
        printf ("usage: remote_lat <connect-to> <message-size> "
                "<roundtrip-count>\n");
        return 1;
    }
    connect_to = argv[1];
    message_size = atoi (argv[2]);
    roundtrip_count = atoi (argv[3]);

    zmq::context_t ctx(1);
  zmq::socket_t s(ctx,ZMQ_DEALER);

  struct timespec ts_dtime;
  unsigned long sec;
int iHighWaterMark=0;
    s.setsockopt(ZMQ_SNDHWM , &iHighWaterMark,
                         sizeof (int));
    s.setsockopt(ZMQ_RCVHWM , &iHighWaterMark,
                                              sizeof (int));

    s.connect(connect_to);


    for (i = 0; i != roundtrip_count; i++) {
      zmq::message_t msg(message_size+20);
      clock_gettime(CLOCK_REALTIME, &ts_dtime);
      sec=(ts_dtime.tv_sec*1e9)+ ts_dtime.tv_nsec;
      sprintf((char *)msg.data(),"%lu",sec);
      rc = s.send(msg);
        if (rc < 0) {
            printf ("error in zmq_sendmsg: %s\n", zmq_strerror (errno));
            return -1;
        }

        sleep(1);
}
s.close();


    return 0;
}

вывод:

1562125527489432576 1562125527489773568
1562125528489582848 1562125528489961472
1562125529489740032 1562125529490124032
1562125530489944832 1562125530490288896
1562125531490101760 1562125531490439424
1562125532490261248 1562125532490631680
1562125533490422272 1562125533490798080
1562125534490555648 1562125534490980096
1562125535490745856 1562125535491161856
1562125536490894848 1562125536491245824
1562125537491039232 1562125537491416320
1562125538491229184 1562125538491601152
1562125539491375872 1562125539491764736
1562125540491517184 1562125540491908352
1562125541491657984 1562125541492027392
1562125542491816704 1562125542492193536
1562125543491963136 1562125543492338944
1562125544492103680 1562125544492564992
1562125545492248832 1562125545492675328
1562125546492397312 1562125546492783616
1562125547492543744 1562125547492926720
1562125564495211008 1562125564495629824
1562125565495372032 1562125565495783168
1562125566495515904 1562125566495924224
1562125567495660800 1562125567496006144
1562125568495806464 1562125568496160000
1562125569495896064 1562125569496235520
1562125570496080128 1562125570496547584
1562125571496235008 1562125571496666624
1562125572496391424 1562125572496803584
1562125573496532224 1562125573496935680
1562125574496652800 1562125574497053952
1562125575496843776 1562125575497277184
1562125576496997120 1562125576497417216
1562125577497182208 1562125577497726976
1562125578497336832 1562125578497726464
1562125579497549312 1562125579497928704
1562125580497696512 1562125580498115328
1562125581497847808 1562125581498198528
1562125582497998336 1562125582498340096
1562125583498140160 1562125583498622464
1562125584498295296 1562125584498680832
1562125585498445312 1562125585498842624
1562125586498627328 1562125586499025920

Все находятся в диапазоне 350-450us

1 Ответ

0 голосов
/ 02 июля 2019

Q1: Что я делаю не так?
Я думал, zmq будет по крайней мере так же быстро, как tcp.

Код, ничего.

С точки зрения производительности, ZeroMQ - это фантастика, плюс у него так много функций, что tcp не предоставляет и не будет предоставлять сразу из коробки:

enter image description here

Test-setup " Отправка одного байта ...", кажется, шагает прямо в левый край высокопроизводительной службы сообщений с низкой задержкой:

enter image description here

Давайте сначала поймем латентность и откуда она взялась:

Наблюдаемые итоговые значения задержки являются произведением общей суммы использования ресурсов (распределение ресурсов + операции по управлению пулами ресурсов + манипуляции с данными) и усилий по обработке (все, что мы попытаться сделать с данными, в том числе и со временем, которые наша задача должна была потратить в очереди на ожидание, из-за запланированного многозадачного планирования рабочих модулей, запланированного системным планировщиком, которые не соответствуют нашей нагрузке тестирования, но операционная система должна планировать и выполнить, в соответствии с политикой справедливого планирования и фактическими настройками приоритета процесса) и каналами связи транспортные задержки (связь E2E с задержкой транспорта)

Давайте теперь поймем, с чем мы пытаемся сравнить:

Разница между протоколом управления передачей (необработанный tcp) и ZeroMQ zmq каркас интеллектуальных масштабируемых формальных архетипов связи с богатым набором высокого уровня, распределенное поведение, примерно несколько больших галактик.

ZeroMQ был спроектирован как скорее инфраструктура сигнализации и обмена сообщениями , использующая некоторые из этих многофункциональных наборов поведений , которые совпадают друг с другом - часто изображается неким подобным человеку поведенческим архетипом:

Один PUSH -ес, любое количество присоединившихся контрагентов PULL

Один REQ -ест, кто-то из группы на другом конце телефона REP -лис

Один, даже потенциально один из какой-то более крупной группы агентов, PUB, - означает, что любое количество уже подписанных подписчиков получит такое сообщение SUB.

За подробностями можно ознакомиться с кратким обзором основных концептуальных различий в иерархии [ ZeroMQ менее чем за пять секунд ] Раздел .

Это ничего, что TCP-протокол никогда не сможет обеспечить самостоятельно.

Это комфорт, который нравится платить за незначительное время ожидания. Незначительное? Да, пренебрежимо мало, если сравнивать с многолетним мастерством работы с программным обеспечением, которому каждый должен был бы заплатить за разработку другой, по крайней мере, такой же интеллектуальной системы обмена сообщениями, чтобы конкурировать с ZeroMQ.

Q2: Могу ли я сделать какую-либо настройку, чтобы сделать zmq быстрее?

Может быть, да, а может и нет.

Обновление:
- попробуйте избежать управления идентификацией (tcp также не имеет такой вещи, поэтому измеренные RTT-s являются менее сопоставимыми или значимыми)
- попробуйте избежать способа блокировки HWM-конфигураций (у tcp такого тоже нет)
- может попытаться измерить то же самое по протоколу без протокола tcp (PAIR/PAIR формальный масштабируемый коммуникационный архетип, лучше всего по сравнению с наименее сложными протокольными насосами данных, как inproc:// или ipc:// в случае, если ваш испытательный стенд SandBox должен по-прежнему храните распространенные нелокальные копии и т. д.) Внутренние накладные расходы экземпляра ZeroMQ context, потраченные на .send() соотв. .receive() методы
- может попытаться немного увеличить производительность, используя больше потоков, доступных для Context instance
(другие приемы, снижающие производительность, зависят от характера реального использования - как устойчивость к пропущенным сообщениям, возможность использовать режим операций с одинаковыми значениями, лучшее выравнивание буфера с O / S, приемы с нулевым копированием - все это представляет некоторый интересздесь, тем не менее, необходимо разрешить и поддерживать инфраструктуру Smart ZeroMQ с распределенным поведением в рабочем состоянии, что является гораздо более сложной задачей, чем тривиальная последовательная последовательность в противном случае слепых и изолированных операций на уровне байтов tcp-сокета, поэтому сравнение временвозможно, но сравнивать отдельные драконьи машины класса «драгстер» (ну, лучше автомобиль, даже не автомобиль) с чем-то вроде глобально работающей инфраструктуры распределенного поведения (например, Taxify или Uber, названной здесь просто для использования тривиального (dis-) сходство примерно той же шкалы величин) оставляет числа, сообщающие о явлениях, которые не обеспечивают аналогичного удобства, масштабируемости сценариев использования, почти линейного масштабирования производительности и надежности tиспользование в реальных условиях)
- может добавить больше детерминизма планирования, сделав соответствующий IoTHREADs экземпляр *1114* жестко подключенным к соответствующему (ым) ядру (ям) ЦП, так что общая производительность ввода-вывода никогда не будетисключается из расписания ЦП и остается детерминированно сопоставленным / предварительно заблокированным даже для исключительно выделенных для ядра ЦП - зависит от уровня потребностей и административных политик, если попытаться добиться этого максимального снижения производительности

Для любой настройки производительности необходимо опубликовать MCVE + полностью описанный набор тестов производительности.Отчет с результатами теста на задержку ZeroMQ показывает:

Заключение

В контролируемой среде RDTSC может использоваться инструкция для быстрого измерения времени.Это позволяет нам измерять задержку / плотность для отдельных сообщений, а не вычислять средние значения для всего теста.

Мы использовали этот подход для получения показателей производительности ядра облегченного обмена сообщениями ØMQ (версия 0.1), и мы получилиследующие результаты:

- В случае низкого объема задержка почти равна задержке базового транспорта (TCP): 50 микросекунд.
- Среднее дрожание задержки минимально: 0,225микросекунда.
- Пропускная способность на стороне отправителя составляет 4,8 млн. сообщений в секунду.
- Плотность на стороне отправителя в основном составляет около 0,140 мкс, однако при случайных пиках средняя плотность составляет 0,208 мкс.
- Пропускная способность на стороне получателя составляет 2,7 миллиона сообщений в секунду.
- Плотность на стороне получателя составляет в основном около 0,3 микросекунды.Приблизительно через каждые 100 сообщений поступает новая партия, в результате чего плотность увеличивается до 3-6 микросекунд.Средняя плотность составляет 0,367 микросекунды.

Если в конечной необходимости для латентного бритья можно попробовать , младшую сестру ZeroMQ, созданную Мартином СУСТРИКОМ, со-отец ZeroMQ (в настоящее время поддерживается Afaik кем-то еще)

...