我能从Twisted LoopingCallback中获得的最高保真度约为100ms(.1sec)



[Update]我使用的是Twisted 22.4.0和Python 3.9.6

我正在尝试编写一个异步应用程序,它必须以250Hz的频率运行事件循环。到目前为止,Twisted的速度还不够快,无法用于我的应用程序(但我想知道是否有可能解决这个问题)。在Windows 10 i5笔记本电脑上,我在LoopingCall中能达到的最高频率约为50赫兹。当我调整时,下面的代码在50hz下运行正常;耗时1.002秒";,但在100Hz下,代码通常需要1.5秒才能运行,我需要我的代码能够在.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的异步反应器。事实证明,它仍然需要与上面不使用异步反应器的代码相同的时间。

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

另一张纸条,我确实找到了这张票(https://twistedmatrix.com/trac/ticket/2424)对于Twisted,我发现了[Edit](Twisted-Glyph-的作者选择不转移到基于单调的时间,除非有API更改,据我所知,除了可能与asyncioreactor一起使用时,还没有实现?)。这也让我对使用Twisted作为可靠的高频事件循环产生了其他担忧,例如NTP时钟调整。现在,可能是在引擎盖下使用asyncio(带asyncioreactor)解决了这个问题,但它似乎并没有提供任何速度优势。

[更新2]这可能解决了我的问题:我用下面的代码调整了窗口睡眠分辨率,现在我的LoopingCall似乎在250Hz的1秒内可靠地运行了上面的代码,并且可靠地运行到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的新手,所以我可能使用不正确(异步反应器的使用,或者实际的LoopingCall-尽管LoopingCall看起来很简单)

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

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

发件人:https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod

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

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

注意4:1000Hz:下3秒运行时间(每次3次)

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] 

我使用异步反应器的实现

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
...
...

要解决我的问题:

我用下面的代码调整了窗口睡眠分辨率,现在我的LoopingCall似乎在250Hz的1秒内可靠地运行了上面的代码,并且可靠地运行到1000Hz:

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

相关内容

  • 没有找到相关文章

最新更新