在 Python 中使用 socket.send() 的 UDP 调用非常慢

UDP calls using socket.send() in Python are very slow

我正在尝试在 Linux 环境中编写一个简单的 Python 脚本来测试必须处理包含小负载(12 字节)的高频 UDP 消息的以太网交换机。

基本设计是我有一台笔记本电脑(带 Ubuntu VirtualBox)运行 python 脚本使用 socket.recvfrom() 连接到 100Mbps 交换机(Netgear FS105).此交换机连接到桌面 PC(带有另一个 Ubuntu VirtualBox),它发送包含两个字节的 UDP 数据包。这些字节只是一个计数器,它允许我查看数据包在传输后是否被重新排序或丢失。

传输代码如下所示:

    self.sock = socket.socket(socket.AF_INET,
                              socket.SOCK_DGRAM) # UDP

    period_s = 1 / 5000.0 # 5KHz
    while True:
        udp_count = chr ((i >> 8) & 0xFF) + chr( i & 0xFF )
        start_time_s = time.time()
        self.sock.sendto(udp_count, (self.UDP_BROADCAST_IP, self.UDP_PORT))
        remaining_idle_time_s = period_s - start_time_s
        time.sleep(remaining_idle_time_s)
        i += 1

每隔几秒就会发生一次,remaining_idle_time_s 返回负值,因为 self.sock.sendto() 函数花费的时间超过 0.2 毫秒。

使用 cProfile,我可以看到平均 self.sock.sendto() 调用需要 0.14 毫秒,但有时需要长达 3 毫秒!使用 getsockopt() 我可以看到套接字发送缓冲区很大 (212992)。

我该怎么做才能使 sock.sendto() return 更快。我猜测需要 3 毫秒的实例是由于 CPU 决定执行另一项任务。有没有办法防止我的程序发生这种上下文切换?

简短的回答是 sendto() 将花费多长时间,并且您无法阻止内核决定进行上下文切换以服务于更高优先级的 CPU 中断。毕竟,这就是操作系统的作用。这就是 Linux、Linux.

的原因

我想你可以通过优化内存存储来尝试从这块石头中挤出更多的血,这样你通过 sendto() 传输的缓冲区永远不会跨越页面边界,并且总是落入一些方便的缓存中-线;也可能摆弄各种可调 Linux 内核调度参数,并从中得到一些东西。

无论所有这些巫术是否会有所作为,任何人的猜测都与其他人的猜测一样好。也许你可以挤出一纳秒左右。所有的汗水和泪水都值得吗?我不知道。

在我看来,如果尖端性能对您来说如此重要,那么您最不想使用的就是通用操作系统,例如 Linux(因为您的问题被标记为 "Linux"),以及一种基于虚拟机的高级语言,如 Python。您将希望尽可能靠近金属;这意味着 C,也许更适合您的应用程序的平台是专门的实时操作系统,它可以为应用程序提供有保证的性能。

您想在非实时 OS 中执行实时任务(Linux 不是实时 OS)。

Realtime 并不意味着 快速 而是在指定的时间限制内完成任务:即防止上下文切换是一种方法,因为您的任务时间变得可预见的。

使用 C 或编写内核驱动程序来实现它可以使其更快,但不确定。有一些像 RTLinux 这样的硬实时 Linux 实现,但是 运行 如果你的主机不是实时的,它在虚拟机中是没有意义的。

在 Linux 中无法做到这一点,我对此感到非常高兴,因为 防止上下文切换的可能性 意味着每个应用程序都可以阻止 OS 完成所谓的 重要工作 ...

好的,所以我 运行 进行了更多测试,正如上面提到的,这个 t运行smit 限制从根本上受到限制,因为我使用的是 Linux VirtualBox 和 Windows 宿主。

即使我将 Windows 中的 VirtualBox 进程设置为最高优先级并在 VirtualBox 中设置 python 套接字调用 RT,该进程有时仍会中断 > 5ms。

运行 在本机 Linux 机器上,我可以使用 sudo chrt -r -p 99 my_python_process_id 将 t运行smit python 进程设置为实时,程序可以以 10KHz 发送没问题!