Show HN: Runloom – Go-style coroutines for Python free-threaded
Runloom: Bringing Go-Style Coroutines to Free-Threaded Python
Runloom is a high-performance runtime that implements "real" Goroutines for Python 3.13t and beyond. It allows developers to write synchronous-looking blocking code that scales across all available CPU cores without the need for or async.await
🚀 The Core Value Proposition
Imagine writing standard blocking code—using fiber(fn), simple send and recv operations—and deploying millions of these fibers across every core of your processor within a single process.
Runloom achieves this through a combination of:
- Hand-rolled assembly for lightning-fast context switching.
- A C-based work-stealing scheduler.
- A custom netpoll system.
- Optimization for free-threaded Python 3.14t (where the GIL is disabled).
Quick Example: Concurrent Crawling
import runloom
# monkey.patch() transforms blocking stdlib calls (like urlopen)
# into cooperative ones. Instead of blocking the OS thread,
# the fiber is parked on the socket.
runloom.monkey.patch()
def crawl(url):
# This looks blocking, but it's actually cooperative!
body = urlopen(url).read()
print(f"Fetched {len(body)} bytes from {url}")
def main():
for _ in range(64):
runloom.fiber(crawl, "https://example.com")
# Initialize 8 hub threads to utilize 8 real cores on 3.14t (GIL off)
runloom.run(8, main)
📊 Performance: Runloom vs. Go
Tested on a 64-core machine using free-threaded CPython 3.13t with 8 hubs (equivalent to GOMAXPROCS=8).
| Metric | Runloom | Go | Verdict |
|---|---|---|---|
| Spawn (Pure C) | Beats Go | ||
| Spawn (Python) | Go | ||
| Context Switch | Faster | ||
| Channel RT | Competitive | ||
| Gosched | Equal | ||
| Conn/s (Churn) | Parity | ||
| Req/s (Echo) | (C handler beats Go) | ||
| Memory (Parked) | (The main gap) |
The Bottom Line: In terms of scheduling, spawning, and raw throughput, Runloom competes directly with Go. It even outperforms Go in raw C-level spawning. The only significant trade-off is memory: because each suspended fiber must carry a CPython eval frame, the RSS is roughly higher than Go's.
🛠 Technical Architecture
How it Works
Runloom implements an M:N scheduling model, where fibers are mapped onto OS threads (hubs).
- Context Switching: Uses hand-rolled assembly (x86_64 SysV, aarch64) for swaps without syscalls. Fallbacks include Windows Fibers and POSIX
ucontext. - Work-Stealing: Each hub utilizes a Chase-Lev deque. Woken fibers are routed back to their original hub to maintain locality.
- State Management: Captures a
PyThreadStatesnapshot (includingcframe,datastack,contextvars, andexc_info), allowing millions of fibers to coexist without "frame-chain cliffs." - Netpoll: A cross-platform abstraction layer supporting
epoll,kqueue,IOCP,WSAPoll, andselect. - Stall Recovery: If a fiber makes an unexpected blocking call, Runloom detects the stalled hub and recovers it automatically.
Installation & Optimization
pip install runloom
You can tune the runtime based on your needs:
runloom.optimize("throughput"): Maximizes spawn rates usingfiber_fast.runloom.optimize("memory"): Uses smaller, right-sized stacks (default).
⚠️ Honest Limitations
While powerful, Runloom has specific constraints:
- GIL Dependency: To achieve multi-core gains, you must use free-threaded CPython 3.13t. On standard GIL builds, it behaves like
asyncio(single-core). - Python Speed Ceiling: It cannot increase CPython's base speed ( ops/s/core); it simply allows you to hit that ceiling on every core simultaneously.
- Memory Overhead: Higher per-fiber memory usage compared to Go due to the CPython eval frame.
- Preemption: Only occurs at bytecode boundaries. A tight loop in a pure-C extension will not be preempted.
- Platform Maturity: While multi-platform, Linux x86_64 / 3.13t is the primary, most heavily validated target.
🌍 Platform Support Matrix
| OS / Arch | Switch Mechanism | Netpoll | Status |
|---|---|---|---|
| Linux x86_64 | fcontext-asm | epoll | Primary (3.11 3.14t) |
| Linux aarch64 | fcontext-asm | epoll | Tested via QEMU |
| macOS (Intel/ARM) | fcontext-asm | kqueue | Hardware tested (3.14t) |
| FreeBSD/GhostBSD | fcontext-asm | kqueue | Hardware tested (3.12) |
| Windows 10/11 | Fibers | IOCP select | Hardware tested (3.14t) |
| Solaris/Android | ucontext / asm | select / epoll | Under review |
📂 Project Structure
The repository is organized as follows:
src/runloom_c/: The engine (C extension, scheduler, assembly backends, netpoll).src/runloom/: Python API layers (sync,aio,monkey,time).tests/: Comprehensive test suite.examples/: Runnable implementation samples.benchmark/: Performance harness and reports.docs/: Full guides (Quickstart, Channels, M:N Parallelism).
Note on runloom.aio: This provides a bridge to run the runtime on a single-threaded scheduler. It is intended as a zero-rewrite migration path from asyncio, not for multi-core acceleration. For speed, use the Sync API with run(n, main).