1

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')
4

0 回答 0