我们如何在Linux中使用sleep()来保持我们的CPU使用率合理,同时仍然具有适当的时间精度?

问题

我试图testing一个使用UDP数据包以预定速率通信的系统。 我希望能够使用具有设置数据包速率的Pythontesting工具来testing此系统。 采样率可能是20包/秒,或4500包/秒等。

在一些简单的testing中,我确定我的Windows机器可以在本地主机上每秒传递150,000个UDP数据包,所以我可以将其视为实验的上限。

让我们从这个shell结构开始创build一个速率限制器。 这段代码主要是由这个线程中的代码激发的。

方法1

import time, timeit class RateLimiter: def __init__(self, rate_limit): self.min_interval = 1.0 / float(rate_limit) self.last_time_called = None def execute(self, func, *args, **kwargs): if self.last_time_called is not None: # Sleep until we should wake up while True: now = timeit.default_timer() elapsed = now - self.last_time_called left_to_wait = self.min_interval - elapsed if left_to_wait <= 0: break time.sleep(left_to_wait) self.last_time_called = timeit.default_timer() return func(*args, **kwargs) 

你可以像这样使用这个助手类:

 self._limiter = RateLimiter(4500) # 4500 executions/sec while True: self._limiter.execute(do_stuff, param1, param2) 

timeit.default_timer()的调用是Python中的一个快捷方式,它为您提供了最高精度的计时器,可以在Windows和Linux上提供大约1e-6秒的准确度,这是我们所需要的。

方法1的性能

在这种方法中, sleep()可以在没有吃掉CPU周期的情况下为你购买时间,但是它会影响你延迟的准确性。 这个评论显示了Windows和Linux之间关于sleep()小于10ms的区别。 总之,Windows的sleep()仅适用于1ms或更长的值(任何一个被认为是零),但睡眠时间一般小于所要求的睡眠时间,而在Linux sleep()更精确,但一般睡眠略多于要求的时间。

上面的代码在我的Windows机器上是准确的,但是对于更快的速度是低效的。 当我在testing中要求4500包/秒的速率时,我得到了4466包/秒(0.75%的误差)的中值。 但是 ,对于速度超过1000Hz的速率, sleep()的调用时间为零,因此RateLimiter会烧毁CPU周期,直到超过等待时间。 不幸的是,我们别无select,因为我们无法在Windows中使用小于1ms的非零睡眠时间。

在Linux中, sleep()的调用花费的时间比请求的要长,产生3470个数据包/秒的中值(22.8%的错误)。 尽pipeLinux中的sleep()需要比预期更长的时间,但要求更高的速率(如6000Hz)产生的真实速率高于4500,所以我们知道它有能力实现目标速率。 问题在于我们的sleep()值,必须纠正为低于我们的预期。 我进行了另一个testing,使用以下(坏)的方法。

方法2

在这个方法中,我们从不睡觉。 我们咀嚼CPU周期直到时间stream逝,这导致Python使用100%的核心运行:

 def execute(self, func, *args, **kwargs): if self.last_time_called is not None: # Sleep until we should wake up while True: now = timeit.default_timer() elapsed = now - self.last_time_called left_to_wait = self.min_interval - elapsed if left_to_wait <= 0: break # (sleep removed from here) self.last_time_called = timeit.default_timer() return func(*args, **kwargs) 

方法2的性能

在Linux中,这产生了4488个数据包/秒(0.26%的错误)的中间速率,这与Windows一样,但以同样的方式吃掉CPU,所以它实际上是低效的。

问题

这是我所得到的。 我们如何在Linux中使用sleep()来保持我们的CPU使用率合理,同时仍然具有适当的时间精度?

我认为这将涉及到某种监督和赔偿过程,但我不确定如何去实施这样的事情。 有没有一个标准的方法来处理这种纠错问题?

确保这一点的唯一方法是使用实​​时操作系统调度。 否则,你处于调度程序的仁慈状态,并可能随时被抢占(例如,如果有一些高优先级/低优先级的进程正在吃掉你的CPU周期)。 事实上, sleep()只是一个方便的方法来要求抢占一个特定的持续时间。 总是有可能你会睡得比你问的要长得多。 这就是为什么Windows甚至不会尝试睡眠<1ms; 一旦你考虑到调度程序的不确定性,它就无法达到这个精度。 开箱即用,Linux也不是,但是可以通过配置(通过sched_setscheduler(2) )来进行实时操作,所以如果你问的话,它会进行尝试。