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

时间:2015-04-09 02:02:06

标签: python linux udp

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

基本设计是我有一台笔记本电脑(w / 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.14ms,但有时需要3ms!使用getsockopt()我可以看到套接字发送缓冲区很大(212992)。

我该怎么做才能让sock.sendto()更快地返回。我猜测需要3ms的实例是由于CPU决定做另一项任务。有没有办法阻止我的程序进行上下文切换?

3 个答案:

答案 0 :(得分:1)

简短的回答是,sendto()将花费很长时间,并且你无法阻止内核决定进行上下文切换以便为更高优先级的CPU中断服务。毕竟,那就是操作系统的功能。这就是Linux,Linux的原因。

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

无论所有伏都教都能实现什么,任何人的猜测都和其他人一样好。也许你可以挤出纳秒或者也可以。所有的汗水和泪水都值得吗?我不知道。

在我看来,如果前沿性能对您来说非常重要,那么您最不想使用的就是通用操作系统,例如Linux(因为您的问题被标记为" Linux& #34;),以及像Python这样的基于虚拟机的高级语言。你想要尽可能接近金属;这意味着C,甚至更适合您的应用程序的平台可能是一个专门的实时操作系统,它可以保证应用程序的性能。

答案 1 :(得分:0)

您希望在非实时操作系统中执行实时任务(Linux不是实时操作系统)。

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

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

在Linux中无法做到这一点我很高兴,因为阻止上下文切换的可能性意味着每个应用程序都可以阻止操作系统执行其所谓的重要工作 ....

答案 2 :(得分:0)

好的,所以我运行了一些测试,如上所述,这个传输限制基本上受到我使用带有Windows主机的Linux VirtualBox这一事实的限制。

即使我在Windows中使VirtualBox进程具有最高优先级并使得python套接字在VirtualBox中调用RT,该进程仍然会因为>而中断。有时5毫秒。

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