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 channelsChan(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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

runloom-0.2.0.tar.gz (1.6 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl (975.6 kB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.38+ x86-64

runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl (966.8 kB view details)

Uploaded CPython 3.13tmanylinux: glibc 2.38+ x86-64

File details

Details for the file runloom-0.2.0.tar.gz.

File metadata

  • Download URL: runloom-0.2.0.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for runloom-0.2.0.tar.gz
Algorithm Hash digest
SHA256 75d09d2a1fa6d3da4db1207a58e0ef8a89099797e20ec4c1e3d9fc35bb274f2c
MD5 8d7240f97e084a399e9fc8e8802b5ab8
BLAKE2b-256 34f9fd66979261e4e663b9179b6aedf6fb5f690154df9ef63998de915e051916

See more details on using hashes here.

File details

Details for the file runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl.

File metadata

File hashes

Hashes for runloom-0.2.0-cp314-cp314t-manylinux_2_38_x86_64.whl
Algorithm Hash digest
SHA256 9c1d533142ec0b11cc7453a7d1b1c9bbfbebbe0962a5d0d67154da8f8aeb4e5b
MD5 dffd07a984cbeb3ed0f389479b20d567
BLAKE2b-256 9f7897e98d4e3076ee1cdf9a425516f0c46c93667eeb06f9ea471aa0bd940fc0

See more details on using hashes here.

File details

Details for the file runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl.

File metadata

File hashes

Hashes for runloom-0.2.0-cp313-cp313t-manylinux_2_38_x86_64.whl
Algorithm Hash digest
SHA256 146d9ccd69ccd98a30864d67fbf68f58395a5a495d663a70a18a9df5f8a0ecac
MD5 979b4e5c88be15003e43462e2a49de18
BLAKE2b-256 d5009c5f9ad0f29cedac2987a29dc24a3c24396756896349bb751203f813e4e5

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

3 files

0.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page