首页 > 解决方案 > zeromq 具有高延迟

问题描述

我正在尝试使用 PUB / SUB 模式使用 zeromq 运行本地低延迟控制循环。

然而,在不同的标准 Ubuntu LTS 安装(从 16.xx - 20.xx)和不同的 PC 上都运行默认内核,我遇到了 0.3ms 到 > 1ms 之间的相当高的延迟。

我的 zeromq 版本是 4.3.2,cppzmq 版本是 4.2(但我在节点和 PHP 绑定方面也遇到了同样的问题)。

示例输出:

TOPIC                  RECV_US              SEND_US
[datawriter_CPLUSPLUS] 1627690147280.142090 1627690147279.663086
[datawriter_CPLUSPLUS] 1627690147380.287109 1627690147379.824951
[datawriter_CPLUSPLUS] 1627690147480.525879 1627690147480.058105
[datawriter_CPLUSPLUS] 1627690147580.789062 1627690147580.251953
[datawriter_CPLUSPLUS] 1627690147680.885010 1627690147680.388916
[datawriter_CPLUSPLUS] 1627690147781.051025 1627690147780.531982
[datawriter_CPLUSPLUS] 1627690147881.116943 1627690147880.676025
[datawriter_CPLUSPLUS] 1627690147981.365967 1627690147980.818115
[datawriter_CPLUSPLUS] 1627690148081.508057 1627690148080.954102
[datawriter_CPLUSPLUS] 1627690148181.571045 1627690148181.091064
[datawriter_CPLUSPLUS] 1627690148281.747070 1627690148281.235107
[datawriter_CPLUSPLUS] 1627690148381.841064 1627690148381.378906
[datawriter_CPLUSPLUS] 1627690148482.018066 1627690148481.541992
[datawriter_CPLUSPLUS] 1627690148582.245117 1627690148581.775879
[datawriter_CPLUSPLUS] 1627690148682.593018 1627690148681.972900

输出来自运行以下我为调试而编写的简单发布者和订阅者程序:

出版商

#include "zhelpers.hpp"
#include <future>
#include <iostream>
#include <string>

int main()
{
    zmq::context_t ctx;
    zmq::socket_t publisher(ctx, zmq::socket_type::pub);
    publisher.bind("tcp://127.0.0.1:3000");

    struct timeval time;
    while (true) {
        gettimeofday(&time, NULL);
        unsigned long long microsec = ((unsigned long long)time.tv_sec * 1000000) + time.tv_usec;
        std::string string = std::to_string(microsec/1E3);
        zmq::message_t message(string.size());
        std::memcpy (message.data(), string.data(), string.size());

        publisher.send(zmq::str_buffer("datawriter_CPLUSPLUS"), zmq::send_flags::sndmore);
        publisher.send(message);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

订户


#include "zhelpers.hpp"
#include <future>
#include <iostream>
#include <string>

int main () {
    zmq::context_t context(1);
    zmq::socket_t subscriber (context, ZMQ_SUB);
    subscriber.connect("tcp://localhost:3000");
    subscriber.setsockopt( ZMQ_SUBSCRIBE, "datalogger_CPLUSPLUS", 1);
    
    struct timeval time;

    while (1) {
        std::string address = s_recv (subscriber);
        std::string contents = s_recv (subscriber);
        
        gettimeofday(&time, NULL);
        unsigned long long microsec = ((unsigned long long)time.tv_sec * 1000000) + time.tv_usec;
        std::string string = std::to_string(microsec/1E3);


        std::cout << "[" << address << "] " << string << " " << contents << std::endl;
    }
    return 0;
}

我的目标延迟低于 100 微秒,而不是当前的 300 - 1300 微秒。上面的延迟对我来说看起来非常高,如果这是我的 zeromq、实现或我的系统/内核配置的问题,我有点想不通。

添加

这是我机器的上下文切换时间,在不同的运行中非常一致:

./cpubench.sh
model name : AMD Ryzen 7 PRO 4750U with Radeon Graphics
1 physical CPUs, 8 cores/CPU, 2 hardware threads/core = 16 hw threads total
-- No CPU affinity --
10000000 system calls in 874207825ns (87.4ns/syscall)
2000000 process context switches in 4237346473ns (2118.7ns/ctxsw)
2000000  thread context switches in 4877734722ns (2438.9ns/ctxsw)
2000000  thread context switches in 318133810ns (159.1ns/ctxsw)
-- With CPU affinity --
10000000 system calls in 525663616ns (52.6ns/syscall)
2000000 process context switches in 2814706665ns (1407.4ns/ctxsw)
2000000  thread context switches in 2402846574ns (1201.4ns/ctxsw)
2000000  thread context switches in 407292570ns (203.6ns/ctxsw)

这是默认安装本地 redis-server 上的一个简单 PHP redis 脚本,其延迟(<100us - 400us)比我可以实现的任何 c++/php/node zeromq 实现低数倍:

1627695114039.4 1627695114039.2
1627695114139.8 1627695114139.6
1627695114240.1 1627695114239.9
1627695114340.3 1627695114340.2
1627695114440.5 1627695114440.3
1627695114540.7 1627695114540.6
1627695114640.9 1627695114640.8
1627695114741.2 1627695114741.1

标签: c++zeromq

解决方案


您正在测量的延迟是从发布者中对 gettimeofday() 的调用到订阅者中的 gettimeofday() 的调用。它会因两台 PC 的 RTC 之间的差异而有所不同,即使与 ntpd 之类的东西同步,也不会完全对齐。如果您让订阅者将消息反射回另一个套接字,那么发布者将能够测量往返时间。

话虽如此,无论如何,我认为延迟不会比您在通过以太网进行的任何数据交换上测量的更好。流量过多地受网络中发生的所有其他事情的摆布,以及有关 PC 的事情。如果您需要保证一台 PC 会在另一台 PC 上发生事件的 100us 内做出反应,那么以太网 / TCPIP / Linux / PC 可能是不适合使用的技术。

例如,如果您的 PC 的 CPU 决定更改电压/时钟模式,则整个 PC 可能会暂时停止,此时发生的时间超过 100us。我已经看到一些 Xeon 系统在发生这种 CPU 模式更改时有 300 毫秒的整机暂停。这些事情超出了操作系统的控制能力——它位于固件层。


推荐阅读