Multi-core async runtime for Python, backed by Tokio. Ships with ferro_io, a
100% drop-in replacement for asyncio (every public symbol resolves; 313
tests verify it, including real asyncpg / SQLAlchemy / FastAPI integration).
# Mode 1 — library-level
import ferro_io as asyncio
asyncio.run(main())# Mode 2 — process-wide (third-party libs also benefit)
import ferro_io
ferro_io.install()
import aiofiles # now uses ferro_io under the hoodCPython's GIL serializes asyncio onto one core. ferro_io moves the scheduler
into Tokio with a multi-thread runtime, so:
- IO workloads stay at the theoretical sleep floor (no slower than stdlib).
- CPU workloads that go through
ferro_io.AsyncRuntime.map_blockingusetokio::task::spawn_blockingand bypass the GIL entirely.
Best of 5 trials. Full matrix with uvloop in benchmarks/RESULTS.md.
| Workload | stdlib asyncio | uvloop | ferro_io | vs stdlib |
|---|---|---|---|---|
| 50 × 50ms IO sleep | 51.62 ms | 51.57 ms | 51.44 ms | ~1× (sleep floor) |
| 200 × 20ms IO sleep | 22.94 ms | 22.00 ms | 21.23 ms | ~1× (sleep floor) |
| 14 CPU chains × 5M LCG iters | 6036 ms | 5578 ms | 6.37 ms | 🔥 947× |
IO workloads: ferro_io and uvloop are both pinned at the theoretical sleep floor —
neither beats physics. CPU workloads: uvloop (libuv) stays GIL-bound like stdlib
because it optimizes the event loop, not CPU parallelism. ferro_io's map_blocking
routes through tokio::task::spawn_blocking, which releases the GIL and saturates
all worker threads.
- Symbol-level: 119/119 asyncio public symbols resolve through
ferro_io. - Real-world programs verified:
TaskGroup, subprocess pipelines, TCP client/server with streams,to_thread,Queueproducer/consumer, gather withreturn_exceptions,wait_for,timeout,Runnerwith contextvars. - Third-party library smoke test:
aiofilesworks underferro_io.install(). - Heavyweight libraries verified against real services under
ferro_io.install():asyncpg(Cython records, prepared statements, transactions, pools),SQLAlchemyasync (greenlet sync→async bridge over asyncpg),FastAPI/Starlette(anyio, contextvars middleware,to_thread). Seetests/test_heavyweights.py. - 307 unit tests + 6 heavyweight integration tests, 0 skipped, 0 failed.
| stdlib | uvloop | trio | ferro_io | |
|---|---|---|---|---|
Drop-in asyncio replacement |
— | ✅ | ❌ | ✅ |
asyncio symbol coverage |
100% | ~98% | 0% | 100% (119/119) |
asyncio.TaskGroup (3.11+) |
✅ | ✅ | n/a (nurseries) | ✅ |
| Multi-core CPU workloads | ❌ GIL | ❌ GIL | ❌ GIL | ✅ (via spawn_blocking) |
| Windows support | ✅ | ❌ | ✅ | ✅ |
httpx / aiohttp / websockets |
✅ | ✅ | partial | ✅ (tested) |
asyncpg (Cython records) |
✅ | ✅ | ❌ | ✅ (tested — see test_heavyweights.py) |
SQLAlchemy async (greenlets) |
✅ | ✅ | ❌ | ✅ (tested) |
FastAPI / Starlette |
✅ | ✅ | partial | ✅ (tested) |
All "tested" cells are backed by tests/test_heavyweights.py, which runs in the
integration CI job against a real Postgres service container and also executes
a matching stdlib-control run so a test-script bug can't masquerade as a ferro_io
incompatibility.
Heavyweight compat tests run in the integration CI job against a real
Postgres service container. To run them locally:
docker compose -f tests/docker-compose.yml up -d
FERRO_IO_INTEGRATION=1 pytest tests/test_heavyweights.py -vGenerate the full matrix (runs each column in a subprocess so uvloop.install()
can't contaminate the others):
pip install uvloop # optional
python benchmarks/bench_matrix.py # writes benchmarks/RESULTS.mdSee benchmarks/RESULTS.md for the latest numbers.
TL;DR: uvloop and ferro_io both sit at the IO sleep floor (within noise of
each other). On workload C, ferro_io's spawn_blocking path bypasses the GIL
while uvloop — like stdlib — remains serialized.
Code that does from asyncio import sleep at module top-level captures the
stdlib reference at import time, before ferro_io.install() can run. To work
around it, call ferro_io.install() as the very first statement in your entry
point — before any third-party import.
python -m venv .venv && source .venv/bin/activate
pip install maturin pytest pytest-asyncio
maturin develop --release
pytest tests/ -v
python benchmarks/bench.pyBuilt with PyO3 0.28 + pyo3-async-runtimes 0.28 + Tokio + maturin. ABI3 wheels cover Python 3.9+.