簡體   English   中英

我們如何在Linux中使用sleep()來保持合理的CPU使用率,同時仍具有不錯的計時精度?

[英]How do we use sleep() in Linux to keep our CPU usage reasonable while still having decent timing accuracy?

問題

我正在嘗試測試使用UDP數據包以預定速率進行通信的系統。 我希望能夠使用具有設定數據包速率的Python測試工具來測試此系統。 采樣率可能是20個數據包/秒或4500個數據包/秒等。

在一些簡單的測試中,我確定Windows計算機可以通過本地主機每秒傳遞高達150,000個UDP數據包,因此出於實驗目的,我可以將其視為上限。

讓我們從這個外殼結構開始創建一個速率限制器。 此代碼主要受此線程中的代碼啟發。

方法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)

在Python中,對timeit.default_timer()的調用是一個快捷方式,它為您的平台提供了最高精度的計時器,在Windows和Linux上都需要約1e-6秒的精度,這是我們所需要的。

方法1的執行

通過這種方法, sleep()可以在不占用CPU周期的情況下為您節省時間,但是會損害延遲的准確性。 此注釋顯示了Windows和Linux之間在sleep()少於10ms的時間方面的差異。 總結一下,Windows的sleep()僅適用於1ms或更大的值(任何小於等於0的值),但通常睡眠時間少於請求的睡眠時間,而在Linux中, sleep()更為精確,但通常處於sleep()所需的時間略長一些

上面的代碼在我的Windows計算機上是准確的,但是對於更快的速度而言效率不高。 當我在測試中要求速率為4500個數據包/秒時,中位數為4466個數據包/秒(0.75%錯誤)。 但是 ,對於高於1000Hz的速率,對sleep()的調用將花費零時間,因此RateLimiter會消耗CPU周期,直到超過等待時間。 不幸的是,我們別無選擇,因為在Windows中不能使用小於1毫秒的非零睡眠時間。

在Linux中,對sleep()的調用花費了比請求更長的時間,產生了3470個數據包/秒的中值(錯誤率為22.8%)。 雖然Linux中的sleep()花費的時間比期望的長,但是請求更高的速率(例如6000Hz)會產生高於4500的真實速率,因此我們知道它能夠達到目標速率。 問題出在我們的sleep()值中,必須將其更正為低於我們預期的值。 我使用以下(不良)方法進行了另一項測試。

方法2

用這種方法,我們從不睡覺。 我們減少了CPU周期,直到時間流逝為止,這導致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使用率,同時仍具有不錯的計時精度?

我認為這將涉及某種監視和補償流程,但是我不確定如何實施這種方法。 是否有解決這種糾錯問題的標准方法?

保證這一點的唯一方法是使用實​​時OS調度。 否則,您將受到調度程序的控制,並可能在任何時候被搶占(例如,如果某些高優先級/低優先級的進程占用了CPU周期)。 實際上, sleep()只是請求特定持續時間搶占的一種便捷方法。 您的睡眠時間總是很可能長於您的要求。 這就是為什么Windows甚至不嘗試睡眠少於1ms的原因。 一旦考慮到調度程序的不確定性,它就無法達到這樣的精度水平。 開箱即用的Linux都不是,但是可以將其配置為(通過sched_setscheduler(2) )設置為實時的,因此如果您提出要求,它將進行嘗試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM