在centos上使用libzmq 4.2.5 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
答案 0 :(得分:0)
问题1:我在做什么错了?
我认为zmq
至少会和tcp一样快。
按代码编写,什么也没有。
在性能方面,ZeroMQ非常棒,并且具有许多功能, tcp
不会,也不会立即提供:
测试设置“ 发送单字节 ...”似乎正步入高性能/低延迟消息传递服务的左侧:
观察到的最终延迟数字是资源使用(资源分配+资源池管理操作+数据操作)和处理努力(由于系统计划程序计划了多任务工作单元的调度,我们试图处理的所有数据(包括时间在内)都是我们的任务必须在等待队列中花费的时间,而不是来自测试工作负载,但是操作系统具有根据公平的调度策略和实际的流程优先级设置来调度和执行)和通信渠道 transport-delays (通信端到端传输延迟)
传输控制协议(原始 tcp
)与具有丰富功能的智能可扩展形式通信原型的ZeroMQ zmq
框架之间的差异一组高级的分布式行为,大约有几个星系很大。
ZeroMQ被设计为一种发信号和消息传递基础结构,它使用了一些功能丰富且相互匹配的行为-经常被某些类似于人类的行为所描绘-原型:
一个 PUSH
-,任意数量的已加入交易对手 PULL
一个 REQ
-est,电话另一端的一个组中的某人 REP
-说谎
PUB
-一个,甚至可能来自更大的一组代理商中的一个,任何数量的已订阅订户都会收到这样的 SUB
留言内容强。
有关详细信息,请阅读 [ZeroMQ hierarchy in less than a five seconds]部分中有关主要概念差异的简短概述。
这不是TCP协议将自己提供的任何内容。
这是一种人们乐于以微不足道的延迟支付的舒适性。微不足道?是的,与许多人多年的最终软件制作技巧相比,这微不足道,任何人都必须为设计另一个至少类似的智能消息传递框架来与ZeroMQ竞争而付出代价。
Q2:我可以做些调整来使
zmq
更快吗?
也许是,也许不是。
更新:
-尝试避免身份管理(tcp也没有这种东西,因此测得的RTT-s具有较小的可比性或意义)
-尝试避免HWM配置的阻塞方式(tcp也没有这种东西)
-可能会尝试在非TCP协议(PAIR/PAIR
正式可扩展通信原型)上进行相同的测量,最好在最不复杂的协议数据泵上进行测量,因为inproc://
或ipc://
您的SandBox测试台仍需要保留分布式的非本地副本等。)ZeroMQ context
-实例在.send()
上花费的内部开销。 .receive()
个方法
-可以尝试通过为Context
实例使用更多线程来允许性能略微提高。
(其他性能去掩盖技巧取决于实际使用情况的性质-作为丢弃消息的鲁棒性,使用合并操作模式的可行性,与O / S更好的缓冲区对齐,零复制技巧-所有这些都引起人们的兴趣在这里,还必须让分布式行为的智能ZeroMQ基础架构保持可操作性,这要比琐碎而孤立的tcp-socket字节级操作的琐碎序列要执行的任务复杂得多,因此比较时间是可能,但是将单个龙龙式的达人级汽车(好一点,甚至是一辆汽车,甚至不是汽车)与具有分布式行为的全球运营基础设施(如Taxify或Uber,在这里命名只是为了利用琐碎的(dis -)近似相同的数量级的相似性)留下报告现象的数字,这些现象无法提供相似的舒适性,用例的可伸缩性,几乎线性的性能缩放和t的鲁棒性他在现实世界中使用)
-可以通过将Context
-实例的相应IoTHREADs
-硬连接到相应的CPU内核上来增加更多的调度确定性,从而使总I / O性能永远不会从CPU-调度并保持确定性地映射/预先锁定(甚至完全锁定在专门用于管理的CPU核上)-如果要执行此最终性能破解,则取决于需求和管理策略的水平
对于任何与性能相关的调整,都需要发布MCVE +完整描述的基准测试套件。 ZeroMQ延迟测试结果报告显示:
结论
在受控环境中,
RDTSC
指令可用于测量 时间很快。这使我们能够测量个体的潜伏期/密度 消息,而不是计算整个测试的平均值。我们已经使用这种方法来获得ØMQ轻量级的性能指标 消息传递内核(0.1版),我们得到以下结果:
-在低容量情况下,延迟几乎与基础传输(TCP)的延迟相同:50微秒。
-延迟的平均抖动极小:0.225微秒。
-发送方的吞吐量是每秒480万条消息。
-发送方的密度大约为0.140微秒,但偶尔出现峰值,平均密度为0.208微秒。
-接收方的吞吐量是每秒270万条消息。
-接收器侧的密度通常约为0.3微秒。大约每100则新批次收到一条消息,导致密度增加 增长到3-6微秒。平均密度为0.367微秒。
如果最终需要剃须时间,可以尝试 nanomsg ,这是ZeroMQ的妹妹,由ZeroMQ的共同父亲Martin SUSTRIK发起(现在由其他人维持着名声) )