|
1 | 1 | # Puring |
| 2 | +TODO - PyPi link |
2 | 3 |
|
3 | | -Experimental async runtime for Python built on Linux io_uring. |
| 4 | +Puring allows true async file i/o natively for Python by bringing Event Loop based on io_uring. Implemented in CPython. |
4 | 5 |
|
5 | | -⚡ ~2× faster file I/O than asyncio thread pools in early benchmarks. |
| 6 | +⚠️ Currently in active development phase, so ABI and internals may change and will be expanded. |
6 | 7 |
|
7 | | -Puring enables true async file I/O in Python without relying on thread pools, |
8 | | -using Linux io_uring and a CPython C-extension runtime. |
9 | | - |
10 | | -⚠️ |
11 | | - |
12 | | -Experimental project \ |
13 | | -APIs and internals may change \ |
14 | | -Used for experimenting with async I/O performance in Python \ |
15 | | -Looking for contributors and feedback \ |
16 | | -⚠️ |
17 | | - |
18 | | -## Why Puring? |
19 | | -* **True Async File I/O:** Unlike epoll-based `asyncio` and `uvloop`, `puring` is based on io_uring, which provides real async I/O without thread pools for files \ |
20 | | -<small> For full explanation, go [here](docs/uring/URING.md) </small> |
21 | | -* **Seamless Integration:** Designed to work as a plug-in for the standard `asyncio` event loop. |
22 | | -* **Low Overhead:** C-implemented request registry with $O(1)$ lookup. |
23 | | -* **Simple Architecture:** Simple layered architecture that makes it easy to understand what is happening internally. |
24 | | -* **Full io_uring support** The goal is to progressively implement all io_uring features. |
25 | | -* **C-Python API** Implemented using CPython C-API for minimal overhead and full control over memory and GIL behavior. |
26 | 8 |
|
27 | 9 | ## Quick Examples: |
28 | 10 | ### Files: |
29 | 11 | ```python |
30 | 12 | async def main(): |
31 | | - file = await puring.open_file(path='testfile.txt') |
32 | | - |
33 | | - data = b'Hello, puring!\n' |
34 | | - await file.write(data=data) |
35 | | - await file.read() |
36 | | - |
37 | | - await file.close() |
| 13 | + async with puring.open_file(path='testfile.txt') as file: |
| 14 | + data = b'Hello, puring!\n' |
| 15 | + await file.write(data=data) |
| 16 | + data = await file.read() |
38 | 17 |
|
39 | 18 | asyncio.run(main(), loop_factory=puring.PuringLoop) |
40 | 19 | ``` |
41 | 20 |
|
42 | 21 | ### Sockets: |
43 | 22 | ```python |
44 | | -HOST = "127.0.0.1" |
45 | | -PORT = 9000 |
46 | | -PAYLOAD = b"hello" |
47 | | - |
48 | 23 | async def main(): |
49 | | - sock = await puring.prep_socket() |
50 | | - |
51 | | - await sock.connect(HOST, PORT) |
52 | | - await sock.send(PAYLOAD) |
53 | | - data = await sock.recv() |
54 | | - print("received:", data) |
55 | | - await sock.close() |
| 24 | + async with await puring.prep_socket() as socket: |
| 25 | + await socket.connect('127.0.0.1', 9000) |
| 26 | + await socket.send(b'hello') |
| 27 | + data = await socket.recv() |
56 | 28 |
|
57 | 29 | asyncio.run(main(), loop_factory=puring.PuringLoop) |
58 | 30 | ``` |
59 | 31 |
|
60 | | -## Comparison |
61 | | -| Feature | asyncio | uvloop | puring | |
62 | | -| ------------------- | ------------ | ------------ | -------- | |
63 | | -| Async files | ❌ threadpool | ❌ threadpool | ✅ native | |
64 | | -| Syscalls | many | many | minimal | |
65 | | -| Kernel batching | ❌ | ❌ | ✅ | |
66 | | -| Zero-copy potential | ❌ | ❌ | ✅ | |
| 32 | +### One of io_uring optimization features - Fixed buffers: |
| 33 | +```python |
| 34 | +async def main(): |
| 35 | + loop = asyncio.get_running_loop() |
| 36 | + buf = bytearray(4096) |
| 37 | + with loop.buffer_mode(mode=puring.BUFFER_MODE.FIXED, buffers=[buf]): |
| 38 | + await simple_socket_example() |
| 39 | + |
| 40 | +asyncio.run(main(), loop_factory=puring.PuringLoop) |
| 41 | +``` |
67 | 42 |
|
| 43 | +### See the whole [user guide](docs/USER_GUIDE.md) |
| 44 | +### See ABI in [documentation page]() and locally [here](). |
| 45 | +### Also you can watch examples inside `docs/examples` and run them under ASAN with |
| 46 | +> make run-examples |
68 | 47 |
|
69 | | -## Quick Install |
| 48 | +## Installation |
70 | 49 | #### Warning! Linux only |
71 | | -> git clone git@github.com:AivazianArtur/puring.git \ |
72 | | -> cd puring \ |
| 50 | +Puring requires linux kernel version 6.11 and Python 3.12 or greater. |
| 51 | +Library is available on PyPI, so use pip to install it: |
| 52 | +> pip install puring |
| 53 | +
|
| 54 | +## Build and use |
| 55 | +To build and install use |
73 | 56 | > make install |
74 | 57 |
|
75 | | -## Architecture |
76 | | -### Why Python needs it |
77 | | -It brings proactor pattern to Python in Linux, that: |
78 | | -* Allows implementation of async file I/O operations. |
79 | | -* Enables designs compatible with upcoming no-GIL Python efforts. |
80 | | - |
81 | | -### Current State |
82 | | -* Core C-engine for Ring management. |
83 | | -* Registry-based request tracking to connect futures with their result from CQE. |
84 | | -* Python C-API bridge for `asyncio.Future` resolution. |
85 | | -* Basic file usage. Brings true Async I/O. |
86 | | -* Basic socket usage. |
87 | | - |
88 | | -### Goal |
89 | | -* Progressive coverage of io_uring features. |
90 | | -### How it works |
91 | | -To read about implementation details, go to [architecture page](docs/ARCHITECTURE.md) |
| 58 | +You can only build by using |
| 59 | +> make build |
92 | 60 |
|
| 61 | +Run tests: |
| 62 | +> make test-all |
93 | 63 |
|
94 | | -## Benchmarks |
95 | | -On simple file benchmarks, `Puring` is showing that even in pre-alpha mode and with many features to come, it is already provide truly async file ops 2x-faster than other Python solutions. \ |
96 | | -For ping-pong benchmark of sockets, puring now shows results close or event better than `uvloop`. It is proof of concept. |
| 64 | +Run tests under ASAN: |
| 65 | +> make test-all-asan |
97 | 66 |
|
98 | | -### File Results: |
| 67 | +While working with code, dont forget to |
| 68 | +> make lint |
99 | 69 |
|
100 | | - |
| 70 | +#### Watch more commands inside `Makefile`. Currently tested only on Fedora 43 with 7.1.5 kernel version. |
101 | 71 |
|
102 | | -### Sockets Results: |
| 72 | +## Architecture |
| 73 | +### io_uring |
| 74 | +Puring is written natively in CPython and brings the new event loop, based on io_uring. \ |
| 75 | +What is io_uring and how it works, explained for Python developers - [here](docs/IO_URING.md) |
| 76 | + |
| 77 | +### Presenting new objects |
| 78 | +- Main: |
| 79 | + - PuringLoop |
| 80 | + - File |
| 81 | + - Socket |
| 82 | +- Helpers: |
| 83 | + - BufferModeCtx |
| 84 | + - StreamStrategyCtx |
| 85 | + - TransferModeCtx |
| 86 | + - ExecutionContextCtx |
| 87 | +- Enums: |
| 88 | + - BUFFER_MODE |
| 89 | + - STREAM_STRATEGY |
| 90 | + - TRANSFER_MODE |
| 91 | + - PAYLOAD_TYPE |
| 92 | + - Resolve Flags |
| 93 | + - Statx Flags |
| 94 | + - StatxMask |
| 95 | + |
| 96 | +Whole documentatation about their purposes and how they work is [here](docs/ARCHITECTURE.md) |
| 97 | + |
| 98 | +### Structure |
| 99 | +Structure of this project is trying to be simple - we have pure c-layer and pure python-layer and layer in between. |
| 100 | +- C-layer - Functionality written entirely in C and basically wrappers aroung liburing ABI |
| 101 | +- Python-layer - By analogy it is layer, written with usage of CPython API and CPython objects. |
| 102 | +- Layer in between - for now here is really only one thing - registry. It is container to hold objects in between. I wish i could say that in between C-layer and Python-layer, but the meaning is not so. We are working with async nature and giving control of the operations to kernel, so we can do our things. To map the result of kernel with what was intended we need some sort of storage - this is registry. |
| 103 | + |
| 104 | +### Domains |
| 105 | +- Ring |
| 106 | +- Event Loop |
| 107 | +- Reader |
| 108 | +- OPS (подтипы) |
| 109 | +- Buffers(внутри написать про фиксед буфер и про открытие/закрытие буфер модов) |
| 110 | +- Execution Context(внутри написать про другие) |
| 111 | +- Registry |
| 112 | +- Timer. То что ниже убрать на отдельную страницу |
103 | 113 |
|
104 | | - |
| 114 | +To read about implementation details, go to [architecture page](docs/ARCHITECTURE.md) |
105 | 115 |
|
106 | | -To learn more, go to [benchmarks documentation](docs/BENCHMARK.md) |
107 | 116 |
|
| 117 | +## Benchmarks |
108 | 118 |
|
109 | 119 | ## Contributing |
110 | | -To start contribute, go to our [contributing guideline](docs/guidelines/CONTRIBUTING.md) |
111 | | - |
112 | 120 | We are looking for help with: |
113 | | -1. Testing on different Linux Distros/Kernels. |
114 | | -2. Sharing experience in memory management, libraries architecture and many other things |
115 | | -3. Write tests and benchmarks \ |
116 | | -And many more, see our [roadmap](docs/ROADMAP.md) |
117 | | - |
118 | | -## Using |
119 | | -### Developer |
120 | | -To install, go to [installation page](docs/guidelines/INSTALLATION.md) \ |
121 | | -To start contribute, go to [developer guideline](docs/guidelines/DEVELOPING.md) and [contribution guideline](docs/guidelines/CONTRIBUTING.md) |
122 | | - |
123 | | -### User |
124 | | -See how to use here - [usage guide](docs/USAGE.md) |
| 121 | +1. Write new functionality. See our [roadmap](docs/ROADMAP.md) - you can add new checks yourself, but create an issue first. |
| 122 | +2. Testing on different Linux Distros/Kernels. If you'll find some issues - create some on GitHub. |
| 123 | +3. Sharing experience in memory management, libraries architecture, cpython, io_uring, epoll and many other things. |
| 124 | +4. Write tests and benchmarks \ |
125 | 125 |
|
| 126 | +* Whole contribution culture should be vaccinated to this project, so if you are experienced in this things - welcome, please. |
| 127 | +* Beware of [developer guideline](docs/guidelines/DEVELOPING.md) |
126 | 128 |
|
127 | 129 | ## Roadmap |
128 | 130 | See [here](docs/ROADMAP.md) |
0 commit comments