Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asyncio: sleep for sub-millisecond interval

I am building a device based on raspberry PI. It will have several concurrent functions that should work simultaneously. In this case using asyncio looks like a reasonable choice (well, I can write all this stuff in C++ with threads, but python code looks much more compact)

One of the functions is to drive a stepper motor via GPIO pulses. These pulses should be 5-10 microseconds long. Is there a way to get asleep for a sub-milliseconds intervals with asyncio sleep?

like image 642
Oleksandr Masliuchenko Avatar asked Feb 02 '19 20:02

Oleksandr Masliuchenko


People also ask

Is Asyncio sleep in milliseconds?

On Linux asyncio uses the epoll_wait system call, which specifies the timeout in milliseconds, so anything sub-millisecond won't work, despite being able to specify it in asyncio. sleep() .

How do you sleep in milliseconds Python?

To sleep for milliseconds with this method, simply use a fractional number. To sleep for 400 milliseconds, for example, use time. sleep(0.4), use time for 60 milliseconds sleep(0.06), for example. Python's sleep() function is a part of the time package.

What is Asyncio sleep ()?

The Sleep() Function Of Asyncio In Python The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.

Does Asyncio block sleep?

sleep(), it will ask the event loop to run something else while our await statement finishes its execution. Hence, you can see in the above code that await asyncio. sleep() is not blocking the execution of the script. Hence, while some part of our code is sleeping , the rest is being executed by the function.


1 Answers

Is there a way to get asleep for a sub-milliseconds intervals with asyncio sleep?

On Linux asyncio uses the epoll_wait system call, which specifies the timeout in milliseconds, so anything sub-millisecond won't work, despite being able to specify it in asyncio.sleep().

You can test it on your machine by running the following program:

import asyncio, os

SLEEP_DURATION = 5e-3  # 5 ms sleep

async def main():
    while True:
        # suspend execution
        await asyncio.sleep(SLEEP_DURATION)
        # execute a syscall visible in strace output
        os.stat('/tmp')

asyncio.run(main())

Save the program e.g. as sleep1.py and run it under strace, like this:

$ strace -fo trc -T python3.7 sleep1.py
<wait a second or two, then press Ctrl-C to interrupt>

The trc file will contain reasonably precise timings of what goes on under the hood. After the Python startup sequence, the program basically does the following in an infinite loop:

24015 getpid()                          = 24015 <0.000010>
24015 epoll_wait(3, [], 1, 5)           = 0 <0.005071>
24015 epoll_wait(3, [], 1, 0)           = 0 <0.000010>
24015 stat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=45056, ...}) = 0 <0.000014>

We see a call to getpid(), two calls to epoll_wait, and finally the call to stat. The first epoll_wait is actually relevant, it specifies the timeout in milliseconds, and sleeps for approximately the desired period. If we lower the sleep duration to sub-milliseconds, e.g. 100e-6, strace shows that asyncio still requests a 1ms timeout from epoll_wait, and gets as much. The same happens with timeouts down to 15 us. If you specify a 14 us or smaller timeout, asyncio actually requests a no-timeout poll, and epoll_wait completes in 8 us. However, the second epoll_wait also takes 8 us, so you can't really count on microsecond resolution in any form of shape.

Even if you use threads and busy-looping, you are likely to encounter synchronization issues with the GIL. This should likely done in a lower-level language such as C++ or Rust, and even so you'll need to be careful about the OS scheduler.

like image 102
user4815162342 Avatar answered Sep 28 '22 06:09

user4815162342