logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·01.11.26
← Back to news

Show HN: Runloom – Go-style coroutines for Python free-threaded

Original article
github.com|35 points|14 comments|by Uptrenda|Jul 10, 2026

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 async or 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:

  1. Hand-rolled assembly for lightning-fast context switching.
  2. A C-based work-stealing scheduler.
  3. A custom netpoll system.
  4. 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).

MetricRunloomGoVerdict
Spawn (Pure C)2.29 M/s2.29 \text{ M/s}2.29 M/s2.10 M/s2.10 \text{ M/s}2.10 M/sBeats Go
Spawn (Python)1.35 M/s1.35 \text{ M/s}1.35 M/s2.10 M/s2.10 \text{ M/s}2.10 M/s0.65×0.65\times0.65× Go
Context Switch≈75 ns\approx 75 \text{ ns}≈75 ns≈560 ns\approx 560 \text{ ns}≈560 nsFaster
Channel RT≈50 ns\approx 50 \text{ ns}≈50 ns≈Parity\approx \text{Parity}≈ParityCompetitive
Gosched≈Parity\approx \text{Parity}≈Parity≈Parity\approx \text{Parity}≈ParityEqual
Conn/s (Churn)75–78 k/s75\text{--}78 \text{ k/s}75–78 k/s75–78 k/s75\text{--}78 \text{ k/s}75–78 k/sParity
Req/s (Echo)596 k/s596 \text{ k/s}596 k/s603 k/s603 \text{ k/s}603 k/s0.99×0.99\times0.99× (C handler beats Go)
Memory (Parked)8.8 KB8.8 \text{ KB}8.8 KB2.7 KB2.7 \text{ KB}2.7 KB≈3.3×\approx 3.3\times≈3.3× (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 3.3×3.3\times3.3× higher than Go's.


🛠 Technical Architecture

How it Works

Runloom implements an M:N scheduling model, where MMM fibers are mapped onto NNN OS threads (hubs).

  • Context Switching: Uses hand-rolled assembly (x86_64 SysV, aarch64) for ≈80ns\approx 80\text{ns}≈80ns 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 PyThreadState snapshot (including cframe, datastack, contextvars, and exc_info), allowing millions of fibers to coexist without "frame-chain cliffs."
  • Netpoll: A cross-platform abstraction layer supporting epoll, kqueue, IOCP, WSAPoll, and select.
  • 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 using fiber_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 (≈80k\approx 80\text{k}≈80k 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 / ArchSwitch MechanismNetpollStatus
Linux x86_64fcontext-asmepollPrimary (3.11 →\rightarrow→ 3.14t)
Linux aarch64fcontext-asmepollTested via QEMU
macOS (Intel/ARM)fcontext-asmkqueueHardware tested (3.14t)
FreeBSD/GhostBSDfcontext-asmkqueueHardware tested (3.12)
Windows 10/11FibersIOCP →\rightarrow→ selectHardware tested (3.14t)
Solaris/Androiducontext / asmselect / epollUnder 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).