Skip to main content

Runloom

Go-style stackful coroutines for Python. Write blocking code — fiber(fn), plain recv/send, no async/await — and run a million of them across every core in one process. Hand-rolled asm context switch + C work-stealing scheduler + netpoll, built for free-threaded Python 3.14t (GIL off).

import threading, runloom
from urllib.request import urlopen
runloom.monkey.patch()

def crawl(url):
    # urlopen() looks blocking -- but monkey.patch() parks the goroutine on the
    # socket instead of the OS thread, so all 64 fetches overlap on real cores.
    body = urlopen(url, timeout=10).read()
    print(threading.get_native_id(), len(body))

def main():
    for _ in range(64):
        runloom.fiber(crawl, "http://example.com")

runloom.run(8, main)   # 8 hub threads -> real cores on 3.14t (GIL off)

Runloom vs Go

Same box (64c, free-threaded CPython 3.13t), 8 hubs / GOMAXPROCS=8, warm steady-state. Go ≈ 2.1 M spawn/s here.

metric runloom Go verdict
spawn — pure C (c_entry) 2.29 M/s 2.10 M/s beats Go
spawn — Python (runloom.fiber) 1.35 M/s 2.10 M/s 0.65×
context switch ~75 ns yield · ~560 ns chan RT ~50 ns Gosched ~parity
conn/s — churn (new conn per req) ~75–78 k/s ~75–78 k/s parity
req/s — keep-alive echo, Python handler 596 k/s 603 k/s 0.99× — parity (C handler beats Go)
memory — empty parked fiber 8.8 KB 2.7 KB 3.3× (the one real gap)

The short story: on spawn, scheduling, and throughput, runloom trades blows with Go and beats it on raw spawn — a stackful coroutine runtime on CPython matching a compiled language even with a Python handler (596 k vs 603 k req/s at saturation; a C handler beats Go). The one honest gap left is memory: a suspended fiber carries a CPython eval frame, ~3.3× Go's per-fiber RSS. Full cross-runtime numbers + cold spawn-vs-N curves: benchmark report · perf summary.

runloom.optimize("throughput")   # runloom.fiber -> max spawn rate (fiber_fast)
runloom.optimize("memory")       # runloom.fiber -> small right-sized stacks (default)

Install

pip install runloom
import runloom      # scheduler + channels, plus monkey/time/context/sync/aio

Prebuilt wheels (no compiler needed) for CPython 3.11–3.14 on Linux (x86_64/aarch64), macOS (arm64/x86_64), Windows (AMD64); source build elsewhere. No runtime dependencies.

What it is

  • Hand-rolled asm context switch (x86_64 SysV, aarch64) — ~80 ns/swap, no syscall; Windows Fibers / POSIX ucontext fallback.
  • M:N work-stealing scheduler (3.13t) — Chase-Lev deque per hub, per-hub MPSC submission, woken goroutines routed back to their origin hub.
  • Per-goroutine PyThreadState snapshot — cframe, datastack, exc_info, contextvars, recursion; a million yielded goroutines share their hub threads with no frame-chain cliff.
  • netpoll — epoll / kqueue / IOCP / WSAPoll / select; goroutines park transparently on fd readiness, lost-wake-free 3-state park-commit.
  • Go-style channels — Chan(capacity), select, for v in ch.
  • Stall isolation + recovery — one unanticipated blocking call stalls only its hub, and the runtime detects + recovers it (default on, 3.13t).
  • monkey.patch() makes blocking stdlib (socket, time, threading, …) cooperative, so existing blocking code runs unchanged.

Already have async def code? The runloom.aio bridge runs it on the single-threaded scheduler (runloom.aio.run(main()) ≈ asyncio.run) — a zero-rewrite port path, not a multi-core speedup (use the sync API with run(n>1, main) for that).

Honest limitations

  • The multi-core win needs free-threaded CPython 3.13t (3.11+ for the frame snapshot at all). On a GIL build runloom still runs — cheap spawn, the goroutine model, netpoll — but single-core like asyncio.
  • runloom doesn't make Python faster per core. CPython's ~80 k pure-Python ops/s/core is a constant it can't raise; it lets one process hit that on every core at once with a blocking model. The scheduler itself is Go-class.
  • Higher memory per goroutine than Go (~3.3× for an empty fiber — the CPython eval frame; a C handler closes most of it).
  • Preemption fires only at Python bytecode boundaries — a goroutine inside a tight pure-C call (e.g. numpy) holds its hub until it returns (same as Go + cgo).
  • Linux x86_64 / 3.13t is the primary, heavily-validated target (2 M-conn runs, fuzzing, sanitizers, formal models); other backends are maintained in-step but less deeply exercised.

Platform support

OS / arch switch netpoll tested
Linux x86_64 fcontext-asm epoll yes — hw, 3.11 / 3.12 / 3.13t / 3.14t (primary)
Linux aarch64 fcontext-asm epoll qemu
macOS x86_64 / arm64 fcontext-asm kqueue hw, 3.14t
FreeBSD / GhostBSD fcontext-asm kqueue hw, 3.12
Windows 10/11 / Server 2022 Fibers IOCP→WSAPoll→select hw, 3.14t
Solaris / Android / other BSD ucontext / asm select / epoll / kqueue review

Docs & layout

Full guide in docs/: Quickstart · Asyncio bridge · Sync API · Channels · M:N parallelism · Cookbook · API reference

Dir Contents
src/runloom_c/ C extension: scheduler, channels, netpoll, asm backends, M:N hubs, stall recovery
src/runloom/ Python layers: aio, sync, monkey, time, runtime
tests/ · examples/ · benchmark/ · docs/ tests · runnable examples · benchmarks + perf harness · docs

Build from source (contributors): pip install -e . from a clone (needs a C compiler; scripts/install.sh / scripts\install.bat bootstrap one). To hack on runloom against free-threaded CPython, use a 3.13t interpreter.

Metadata

Release files for runloom 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for runloom 0.2.0
File Size Uploaded
runloom-0.2.0.tar.gz 1.6 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for runloom 0.2.0
File Interpreter ABI Platform
runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.38+ x86-64 Details
runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl CPython 3.13 CPython 3.13 free-threading Linux glibc 2.38+ x86-64 Details

Total release size: 3.5 MB

Release files / runloom-0.2.0.tar.gz

Download URL runloom-0.2.0.tar.gz
Size 1.6 MB
Tags Source
SHA-256 checksum
How to use checksums
75d09d2a1fa6d3da4db1207a58e0ef8a89099797e20ec4c1e3d9fc35bb274f2c
BLAKE2b-256 checksum
How to use checksums
34f9fd66979261e4e663b9179b6aedf6fb5f690154df9ef63998de915e051916
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl

Download URL runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl
Size 975.6 kB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.38+ x86-64
SHA-256 checksum
How to use checksums
9c1d533142ec0b11cc7453a7d1b1c9bbfbebbe0962a5d0d67154da8f8aeb4e5b
BLAKE2b-256 checksum
How to use checksums
9f7897e98d4e3076ee1cdf9a425516f0c46c93667eeb06f9ea471aa0bd940fc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl

Download URL runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl
Size 966.8 kB
Tags CPython 3.13 CPython 3.13 free-threading Linux glibc 2.38+ x86-64
SHA-256 checksum
How to use checksums
146d9ccd69ccd98a30864d67fbf68f58395a5a495d663a70a18a9df5f8a0ecac
BLAKE2b-256 checksum
How to use checksums
d5009c5f9ad0f29cedac2987a29dc24a3c24396756896349bb751203f813e4e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.2.0 This release

3 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page