繁体   English   中英

我可以从 Twisted LoopingCallback 中获得的最高保真度约为 100 毫秒(0.1 秒)

[英]Highest fidelity I can get out of Twisted LoopingCallback is around 100ms (.1sec)

[更新]我正在使用 Twisted 22.4.0 和 Python 3.9.6

我正在尝试编写一个必须以 250Hz 运行事件循环的异步应用程序。 到目前为止,Twisted 的速度还不足以为我的应用程序工作(但我想知道是否有可能解决这个问题)。 在 Windows 10 i5 笔记本电脑上,我可以在 LoopingCall 中实现的最高频率约为 50hz。 当我调整以下代码以 50hz 运行正常并成功打印出“花了 1.002 秒”,但在 100Hz 时,代码通常需要 1.5 秒才能运行,我需要我的代码能够以 0.004 (250Hz) 运行。

from twisted.internet.task import LoopingCall
from twisted.internet import reactor
import time

class Loop():
    def __init__(self, hz):
        self.hz = hz
        self.lc = LoopingCall(self.fast_task)
        self.num_calls = 0
        self.lc.start(1/hz)
        reactor.run() # **Forgot to add this the first time**

    def fast_task(self):
        if self.num_calls == 0:
            self.start_time = time.time()
        if self.num_calls == self.hz:
            print("Stopping reactor...")
            print(f"took: {time.time() - self.start_time} sec")
            reactor.stop()
            return
        self.num_calls += 1

if __name__ == "__main__":
    l = Loop(100)

上面的代码通常需要大约 1.5 秒才能运行。 我的问题:有什么方法可以在 Windows 上的 Twisted 中加速这个事件循环?

我在 asyncio 中运行了一些类似的代码,asyncio 绝对可以在我的笔记本电脑上处理 250Hz 循环。 所以我接下来尝试的一件事是使用带有 Twisted 的 asyncioreactor。 事实证明,它仍然需要与上面不使用 asyncioreactor 的代码相同的时间。

但是,对于我的用例,我喜欢 Twisted 的简单性——我需要一些 TCP 服务器和客户端以及一些 UDP 服务器和客户端以及其他一些繁重的 I/O 处理。

另一个注意事项,我确实找到了 Twisted 的这张票( https://twistedmatrix.com/trac/ticket/2424 ),我在其中发现了[编辑](Twisted - Glyph - 的作者选择不转向单调基于时间,除非有 API 更改,据我所知,除非与 asyncioreactor 一起使用,否则尚未实现?)。 这也让我担心将 Twisted 用作可靠的高频事件循环,例如 NTP 时钟调整。 现在,可能是在后台使用 asyncio(使用 asyncioreactor)可以解决这个问题,但它似乎并没有提供任何速度优势。

[更新 2]这可能解决了我的问题:我使用以下代码调整了 Windows 睡眠分辨率,现在我的 LoopingCall 似乎可以在 1 秒内以 250Hz 可靠地运行上述代码,并且可靠地高达 1000Hz:

from ctypes import windll
windll.winmm.timeBeginPeriod(1) # This sets the time sleep resolution to 1 ms

[更新3 ]

我已经包含了用于使用 aysncioreactor 创建循环的代码。

注意:您会注意到我正在使用 WindowsSelectorEventLoopPolicy() - 这是因为没有安装最新的 Visual C++ 库(但不确定这是否是重要信息)

注2:我是twisted的新手,所以我可能会错误地使用它(asyncioreactor的使用,或者实际的LoopingCall——虽然LoopingCall看起来很简单)

注 3:我在 Windows 10 v21H2 上运行,处理器:1.6GHz i5

v21H2 在这里很重要,因为它在 v2004 之后:

来自: https ://docs.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod

在 Windows 10 版本 2004 之前,此函数会影响全局 Windows 设置。 对于所有进程,Windows 使用任何进程请求的最低值(即最高分辨率)。 从 Windows 10 版本 2004 开始,此功能不再影响全局计时器分辨率。 对于调用此函数的进程,Windows 使用任何进程请求的最低值(即最高分辨率)。 对于没有调用该函数的进程,Windows 不保证比系统默认分辨率更高的分辨率。

为了看看我是否可以证明这一点,我尝试在不调用 timeBeginPeriod(1) 的情况下运行 Windows Media Player、Skype 和其他程序(想法是另一个进程的另一个程序会设置较低的分辨率,这会影响我的程序。但这并没有改变你在下面看到的时间。

注 4:运行 3 秒的时间(每次运行 3 次)@ 1000Hz:

   asyncioreactor with timeBeginPeriod(1):    [3.019, 3.029, 3.009]
   asyncioreactor with no timeBeginPeriod(1): [42.859, 43.65, 43.152]
   no asyncioreactor with timeBeginPeriod(1): [3.012, 3.519, 3.146]
   no asyncioreactor, no  timeBeginPeriod(1): [45.247, 44.957, 45.325] 

我使用 asyncioreactor 的实现

import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

from twisted.internet import asyncioreactor
asyncioreactor.install()
from twisted.internet.task import LoopingCall

from twisted.internet import reactor
import time

from ctypes import windll
windll.winmm.timeBeginPeriod(1)

class Loop():
    def __init__(self, hz=1000):
        self.hz = hz
    ...
    ...

要解决我的问题:

我使用以下代码调整了 Windows 睡眠分辨率,现在我的 LoopingCall 似乎可以在 1 秒内以 250Hz 可靠地运行上述代码,并且可靠地高达 1000Hz:

from ctypes import windll
windll.winmm.timeBeginPeriod(1) # This sets the time sleep resolution to 1 ms

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM