Streams reorganization
- Lenguaje dominante
- Python
- Estrellas
- 16.5k
- Forks
- 2.4k
- Merge medio
- 17 h 22 min
- PR fusionados (30 d)
- 212
Descripción
I'd like to share my thoughts about streams architecture.
Current state
------------------
Now we have a `StreamReader` class.
`ClientSession` has an `auto_compress` parameter for optional disabling a stream compression (gzip, deflate etc.)
WebSockets have own buffer implementation for reading a data from a stream.
Proposal
---------------
Let's use `StreamReader` for everything.
On the bottom, there is a `raw` stream which is a good data source for both zgipped content and websockets data.
Unzipper provides an own class that is a wrapper around a raw stream. Unzipper's `.read()` calls `raw.read()` and pass the given data chunks into decompressor.
Websocket reader does the same.
The raw stream becomes the only TCP flow control aware structure, all descendants just use it.
Raw -> Unzipped
Raw -> WebSocket
The raw stream is accessible from Unzipped stream by it's `.raw` attribute. Something like [`io.BufferedIOBase.raw`](https://docs.python.org/3/library/io.html#io.BufferedIOBase.raw)
I believe the proposal simplifies the design.
Optimizations
-------------------
Since asyncio 3.7 we have an advanced [`BufferedProtocol`](https://docs.python.org/3/library/asyncio-protocol.html#buffered-streaming-protocols) (old good `asyncio.Protocol` for old Python's can be supported easily on top of it, the price is more memory copy operations).
For reducing mem-copy `BufferedProtocol` *should* use mem structures like Linux Kernel [SLAB allocators](https://en.wikibooks.org/wiki/The_Linux_Kernel/Memory#Slab_Allocator), the protocol was *designed* for it.
A simplified idea is:
- Allocate a large memory page (4Kb or more, we can start from a simple scenario but optimize an allocator for 4MB data chunks later)
- Fetch a data chunk into an allocated page
- Free a page when all page's data is consumed by a raw `StreamReader`
P.S.
IIRC `nginx` uses an interesting custom allocator strategy: it has a memory context tightly coupled with HTTP request. The memory is never released until the request is gone, then all memory allocated by the request processing goes away. We cannot steal the NGINX memory strategy as-is: a lot of Python objects are created an released during HTTP request life-cycle but we can optimize at least `StreamReader` implementation for this case.
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.