python 3.9.5 (anyio, asyncio, trio) windows 10
除了在类中作为任务运行的另一个异步循环之外,我还试图用来anyio.sleep()
限制我的主应用程序循环的运行速度。我注意到当我减少睡眠时间(提高刷新率)时,asyncio 似乎报告了不正确的、非常短的睡眠时间(高刷新率),而 trio 似乎报告了更多的预期值。
此外,当启动函数循环周期低于主循环刷新率时,asyncio 似乎会给出不可预测的结果。在高于 40 hz 的情况下,asyncio 的刷新速度似乎比预期的要快,在输入高刷新率时会呈指数级恶化。
在下面的代码示例中,FPSCounter 只是一个辅助类。整个块是可复制粘贴运行的。
以下是使用底部代码的一些不同刷新率的测试结果。
********** start hz: 33.33 / main hz 60.0 **********
============= trio =============
start function refresh rate (hz)
avg: 32.111337719289175
max: 32.20671951236294
min: 32.01595592621541
main function refresh rate (hz)
avg: 57.98613394545777
max: 58.00646191985787
min: 57.96580597105768
============= asyncio =============
start function refresh rate (hz)
avg: 58.18328943732051
max: 58.22729607295037
min: 58.13928280169065
main function refresh rate (hz)
avg: 58.184786967810574
max: 58.22527199837126
min: 58.144301937249885
********** start hz: 40.0 / main hz 240.0 **********
============= trio =============
start function refresh rate (hz)
avg: 39.234583680312994
max: 39.38072626526089
min: 39.0884410953651
main function refresh rate (hz)
avg: 215.89497360297648
max: 216.52241650591293
min: 215.26753070004003
============= asyncio =============
start function refresh rate (hz)
avg: 64.0348432659224
max: 64.04419798485424
min: 64.02548854699056
main function refresh rate (hz)
avg: 191.6032335310934
max: 192.06711593299164
min: 191.13935112919512
********** start hz: 80.0 / main hz 240.0 **********
============= trio =============
start function refresh rate (hz)
avg: 74.98626248669126
max: 75.31187257068325
min: 74.66065240269926
main function refresh rate (hz)
avg: 220.92863862387009
max: 222.9905452008835
min: 218.86673204685667
============= asyncio =============
start function refresh rate (hz)
avg: 461.9392486786134
max: 732.8401458173149
min: 191.0383515399119
main function refresh rate (hz)
avg: 460.52822045374194
max: 730.0229804121695
min: 191.0334604953143
import anyio
from time import perf_counter_ns
from collections import deque
class FPSCounter:
def __init__(self):
self.current_fps = 0
self.previous_fps = 0
self.last_time = 0
self.update_interval = 1 #sec
self.frame_times = deque()
self.intervals = []
self.history = []
def new_frame(self):
self.frame_times.append(perf_counter_ns())
def update_fps(self):
if (len(self.frame_times) < 2):
if(len(self.frame_times) == 0 or
(perf_counter_ns() > (self.frame_times.pop() + 2*1_000_000_000*self.update_interval))):
self.previous_fps = self.current_fps
self.current_fps = 0
else:
if (self.last_time == 0):
self.last_time = self.frame_times.popleft()
while(len(self.frame_times) > 0):
t = self.frame_times.popleft()
if t > self.last_time:
self.intervals.append(t-self.last_time)
self.last_time = t
if len(self.intervals) == 0: return
avg = sum(self.intervals) / len(self.intervals)
self.previous_fps = self.current_fps
self.current_fps = 1_000_000_000.0 / float(avg)
self.intervals.clear()
self.history.append(self.current_fps)
async def start(fps: float, counter: FPSCounter, duration):
last_time = anyio.current_time()
with anyio.move_on_after(duration) as scope:
while True:
counter.new_frame()
last_time = anyio.current_time()
# group of tasks to do this loop
async with anyio.create_task_group() as tg:
# sleep to cause refresh rate
await anyio.sleep(1.0/fps - anyio.current_time() + last_time)
# do stuff
async def main(start_loop_hz, main_loop_hz):
fps_start = FPSCounter()
fps_main = FPSCounter()
last_fps_update = anyio.current_time()
start_time = anyio.current_time()
last_time = anyio.current_time()
duration = 3.0
# main group to start background 'start' coro
async with anyio.create_task_group() as maintg:
maintg.start_soon(start, start_hz, fps_start, duration)
# main app loop
with anyio.move_on_after(duration) as scope:
while anyio.current_time() < (start_time+duration):
fps_main.new_frame()
last_time = anyio.current_time()
if (anyio.current_time() - last_fps_update) > 1.0:
last_fps_update = anyio.current_time()
fps_main.update_fps()
fps_start.update_fps()
# sleep to cause the refresh rate for main loop
await anyio.sleep(1.0/(main_hz) - anyio.current_time() + last_time)
# do stuff in main app loop
fps_start_avg = sum(fps_start.history) / len(fps_start.history)
fps_start_max = max(fps_start.history)
fps_start_min = min(fps_start.history)
print(f'start function refresh rate (hz)\navg: {fps_start_avg}\nmax: {fps_start_max}\nmin: {fps_start_min}')
fps_main_avg = sum(fps_main.history) / len(fps_main.history)
fps_main_max = max(fps_main.history)
fps_main_min = min(fps_main.history)
print(f'main function refresh rate (hz)\navg: {fps_main_avg}\nmax: {fps_main_max}\nmin: {fps_main_min}')
# exit()
if __name__ == '__main__':
start_hz = 33.33 # refresh rate for the start function loop
main_hz = 60.0 # refresh rate for the main loop
print(f'********** start hz: {start_hz} / main hz {main_hz} **********')
print('============= trio =============')
anyio.run(main, start_hz, main_hz, backend='trio')
print('============= asyncio =============')
anyio.run(main, start_hz, main_hz, backend='asyncio')