3
  • I am trying to have a function run periodically on the event loop
  • It is vital for the function to be called at the rate specified
  • I'm recursively scheduling the function to run using asyncio.call_later
  • It appears that asyncio.call_later does not call the function with the correct delay
  • The timer handle returned by call_later confirms that the delay is greater than expected

Why is the function not being called at the specified rate?

import asyncio
import time

def print_fps():
    global last_time

    time_now = time.time()
    dt = time_now - last_time
    if dt > 0:
        print(f"fps: {round(1/dt, 3)} [{round(dt,3)}s], target: {fps} [{round(1/fps,3)}s]")
    else:
        print("dt = 0")
    last_time = time_now

def loop_func():
    # print fps info
    print_fps()

    # schedule next iteration of loop
    loop.call_later(1/fps, loop_func)

async def main():
    loop_func()

last_time = time.time()
fps = 60

loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()

Output:

fps: 31.446 [0.032s], target: 60 [0.017s]
fps: 32.693 [0.031s], target: 60 [0.017s]
fps: 32.172 [0.031s], target: 60 [0.017s]
fps: 31.663 [0.032s], target: 60 [0.017s]
fps: 32.001 [0.031s], target: 60 [0.017s]
fps: 30.549 [0.033s], target: 60 [0.017s]
fps: 34.486 [0.029s], target: 60 [0.017s]
fps: 31.366 [0.032s], target: 60 [0.017s]
fps: 31.743 [0.032s], target: 60 [0.017s]
fps: 30.057 [0.033s], target: 60 [0.017s]

cProfile reveals nothing is blocking the event loop

not-matt
  • 31
  • 2
  • 2
  • It might be due to system clock resolution. Related [answer](https://stackoverflow.com/questions/65715878/is-asyncio-sleepdelay-guarenteed-to-sleep-for-at-least-delay-seconds/65727201#65727201) – alex_noname Jul 31 '21 at 19:06
  • 1
    @alex_noname You're dead on. Sleep interval is set by the system, which was in this case Windows with about 15ms minimum sleep period. Thank you for your help! – not-matt Aug 01 '21 at 12:54
  • Well, doesn't running a function (print_fps in this case) and scheduling the next call within the current call introduce a drift? ideally you would not want to wait for any logic, not a function call nor the function that schedules for the next call, to schedule the next call, as it would mean that your time between wake ups is not the interval that you specify to `.call_later` but rather that interval plus a small constant. – matanster Jan 22 '22 at 18:28

0 Answers0