Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread-based sender #31

Closed
jabdoa2 opened this issue Nov 17, 2017 · 5 comments
Closed

Thread-based sender #31

jabdoa2 opened this issue Nov 17, 2017 · 5 comments

Comments

@jabdoa2
Copy link
Contributor

jabdoa2 commented Nov 17, 2017

In MPF we use pyserial-asyncio to write to serials with up to 6MBaud/s. Unfortunately, we fail to saturate the serial as soon as the asyncio loop is not completely idle (even idle it is far from perfect). This is mostly caused by very small serial buffers in the linux kernel (1024 bytes by default) which can only be adjusted at compile time. Saturating a 6M serial with a 1024 byte buffer requires nearly 6.000 wake-ups per second with very low jitter which is the reason why this will never work properly.

To solve this we use separate threads (using asyncio's run_in_executor). Would you be up to support that in pyserial-asyncio? I would port our sender code to pyserial-asyncio in that case.

@d21d3q
Copy link

d21d3q commented Nov 17, 2017

I'm curious if you have done some test running asyncio on uvloop?

@jabdoa2
Copy link
Contributor Author

jabdoa2 commented Nov 17, 2017

No we did not. This is not exactly an asyncio issue. It works fine. The situation might improve slightly with a faster loop and certainly works on fast hardware and an idle loop. However, in practice, an asyncio process with "only" 10% CPU utilization fails to write more then a few kBaud of serial data because of small buffers. Instead the StreamWriter will buffer all the data and the process will run out of memory after some minutes if you are uncareful.

In synchronous writes the user space send buffer is used and the kernel buffer never underruns. Even with a 90% busy asyncio loop. I guess this is a more general non-blocking I/O issue (at least on linux).

@stv0g
Copy link

stv0g commented May 3, 2019

I agree point of pyserial-asyncio is asynchronous not concurrent operation.

This request is out of scope of pyserial-asyncio.
Shall we close the issue?

@jabdoa2
Copy link
Contributor Author

jabdoa2 commented May 3, 2019

I don't think that is that much of a difference in practice. Currently, pyserial-asyncio is unsuited for high throughput applications. In the mission pinball framework (a machine to drive pinball machines) we implemented a version which runs the sender thread in an asyncio executor (aka different thread). That allows sending at full speed but still using the same interface by scheduling via a Queue. In my opinion this is something that belongs in pyserial-asyncio.

It is not uncommon to drive a serial at the bandwidth limit (think about pinball DMDs for example). Same as it is not uncommon to have latency sensitive serials (think about switch inputs in a pinball) which were also not supported before we added support (pyserial/pyserial@1b2658a).

All I'm offering is to upstream our implementation. Given that pyserial seems to move very slowly (i.e. there still is no release containing our low-latency feature from last year) it looks like there is not interest in upstreaming new features. That is why I ask before making the effort.

@rob-smallshire
Copy link
Collaborator

Out of scope

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants