Open SerodioJ opened 1 month ago
This is a bad MRE for uvloop, but it does look like there's a different behavior from asyncio here.
MRE is:
import socket
import uvloop
import asyncio
async def main():
s1, s2 = socket.socketpair()
with s1, s2:
reader, writer = await asyncio.open_connection(sock=s1)
writer.write(b"\x00")
s2.recv(1)
asyncio.run(main())
print("asyncio done")
uvloop.run(main())
print("uvloop done")
Looks like the bug is here: https://github.com/MagicStack/uvloop/blob/master/uvloop/handles/stream.pyx#L430
uvloop only writes to the socket if the high water is reached, otherwise it schedules it for the next event loop cycle which never happens
asyncio attempts to send it then adds it to the buffer if anything is not sent https://github.com/python/cpython/blob/main/Lib/asyncio/selector_events.py#L1067
MRE is:
import socket import uvloop import asyncio async def main(): s1, s2 = socket.socketpair() with s1, s2: reader, writer = await asyncio.open_connection(sock=s1) writer.write(b"\x00") s2.recv(1) asyncio.run(main()) print("asyncio done") uvloop.run(main()) print("uvloop done")
Looks like the bug is here: https://github.com/MagicStack/uvloop/blob/master/uvloop/handles/stream.pyx#L430
uvloop only writes to the socket if the high water is reached, otherwise it schedules it for the next event loop cycle which never happens
asyncio attempts to send it then adds it to the buffer if anything is not sent https://github.com/python/cpython/blob/main/Lib/asyncio/selector_events.py#L1067
I've already optimized this part and made it inline with original asyncio: https://github.com/MagicStack/uvloop/pull/619 Perhaps it even fixes the bug, maybe you could try.
The thing is, I've also made a few other nice optimizations a couple month ago (check out the latest PRs) that significantly improve uvloop performance! However, nobody gave any feedback. uvloop seems a little abandoned now.
The thing is, I've also made a few other nice optimizations a couple month ago (check out the latest PRs) that significantly improve uvloop performance! However, nobody gave any feedback. uvloop seems a little abandoned now.
I'll take a look this week!
The thing is, I've also made a few other nice optimizations a couple month ago (check out the latest PRs) that significantly improve uvloop performance! However, nobody gave any feedback. uvloop seems a little abandoned now.
I'll take a look this week!
Thank you, those performance optimizations are really cool!
It would make sense to update benchmark graph on the main page https://raw.githubusercontent.com/MagicStack/uvloop/master/performance.png after they are merged.
I hope this issue can be resolved.
PYTHONASYNCIODEBUG
in env?:I am opening this issue from an issue that I had previously opened in the uvicorn repository
When running a FastAPI app with uvicorn default settings, which uses uvloop, I started to notice that sometimes when handling multiple requests for the same path the response took longer than expected to arrive at the client. This issue became more noticeable when I tried using WebSockets to improve performance by returning the result in parts over the same connection instead of making multiple HTTP requests.
I made a mini FastAPI app that represents the situation of my app, a WebSocket client, and a HTTP Client.
I will try to work on a Minimal Reproducible Example.
Example Code
FastAPI App Code (server.py)
HTTP Client Code (http.py)
WebSocket Client Code (ws.py)
Experiment Instructions
Start server with uvloop (default)
Run clients
Start server with asyncio
Run clients
Results
uvloop
HTTP (some times the times are OK -> 3, 6, 9 and 12)
WebSockets
asyncio
HTTP (the grouping behavior was not observed in any attempt)
WebSockets
Environment
I am using Ubuntu 22.04 and a Python 3.12.5 environment created via conda (4.10.3). pip freeze output: