プログラミング
Show HN: Runloom – Python用のGo風コルーチン(フリー・スレッド対応)
Show HN: Runloom – Go-style coroutines for Python free-threaded (github.com)
要約
Runloomは、Python 3.14以降のフリー・スレッド環境(GILオフ)向けに、Go風のスタックフルコルーチンを提供します。async/awaitを使わずにブロッキングコードを記述でき、1つのプロセス内で数百万ものコルーチンを各コアで並行実行可能です。手書きのアセンブリによるコンテキストスイッチとC言語によるワーク・スティーリング・スケジューラを組み合わせ、Goに匹敵するパフォーマンスを実現しています。
全文翻訳
Runloom
Python用のGo風スタックフルコルーチン。
ブロッキングコード(fiber(fn)、プレーンなrecv/send、async/awaitなし)を記述し、1つのプロセス内の全コアで数百万個実行できます。
手書きアセンブリによるコンテキストスイッチ + C言語によるワーク・スティーリング・スケジューラ + netpoll、フリー・スレッドPython 3.14t(GILオフ)向けに構築。
import threading, runloom
from urllib.request import urlopen
runloom.monkey.patch()
def crawl(url):
# urlopen()はブロッキングに見えるが、monkey.patch()はゴルーチンをOSスレッドではなく
# ソケットに駐車するため、64回のフェッチすべてが実際のコアでオーバーラップする。
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つのハブスレッド -> 3.14t(GILオフ)のリアルコア
Runloom vs Go
同じマシン(64コア、フリー・スレッドCPython 3.13t)、8ハブ/GOMAXPROCS=8、ウォームな定常状態。
Go ≈ 2.1 M spawn/s ここで。
メトリック
spawn — 純粋C (c_entry)
2.29 M/s
Go: 2.10 M/s
勝っている
spawn — Python (runloom.fiber)
1.35 M/s
Go: 2.10 M/s
0.65倍
コンテキストスイッチ
~75 ns
yield · ~560 ns
chan RT
~50 ns
Gosched
ほぼ同等
conn/s — チャーン(リクエストごとに新しい接続)
~75–78 k/s
Go: ~75–78 k/s
ほぼ同等
req/s — キープアライブ エコー、Pythonハンドラ
596 k/s
Go: 603 k/s
0.99倍
— ほぼ同等
(CハンドラはGoに勝る)
メモリ — 空の駐車されたファイバー
8.8 KB
Go: 2.7 KB
3.3倍
(唯一の実際のギャップ)
短い話:スポーン、スケジューリング、スループットにおいて、RunloomはGoと互角に戦い、生のスポーンではGoを上回る — コンパイル言語に匹敵するCPython上のスタックフルコルーチンランタイム。Pythonハンドラを使用しても(596k vs 603k req/s)、CハンドラではGoを凌駕する。
唯一残された正直なギャップはメモリだ。中断されたファイバーはCPythonのevalフレームを保持しており、GoのファイバーあたりRSSの約3.3倍になる。
完全なクロスランタイム数値 + コールドスポーン対N曲線:ベンチマークレポート · パフォーマンス概要。
runloom.optimize("throughput") # runloom.fiber -> 最大スポーンレート (fiber_fast)
runloom.optimize("memory") # runloom.fiber -> 小さな適切なサイズのスタック (デフォルト)
インストール
pip install runloom
import runloom # スケジューラ + チャンネル、およびmonkey/time/context/sync/aio
CPython 3.11–3.14 (Linux x86_64/aarch64, macOS arm64/x86_64, Windows AMD64) 用のプリビルドホイール(コンパイラ不要)。ソースビルドはそれ以外。
ランタイム依存関係なし。
それが何であるか
手書きアセンブリコンテキストスイッチ(x86_64 SysV, aarch64) — 約80 ns/swap、システムコールなし;Windows Fibers / POSIX ucontextフォールバック。
M:Nワーク・スティーリング・スケジューラ(3.13t) — ハブごとのChase-Lev deque、ハブごとのMPSCサブミッション、ウェイクされたゴルーチンは元のハブにルーティングされる。
ゴルーチンごとのPyThreadStateスナップショット — cframe, datastack, exc_info, contextvars, recursion;数百万のyieldされたゴルーチンは、フレームチェーンの断崖なしにハブスレッドを共有する。
netpoll — epoll / kqueue / IOCP / WSAPoll / select;ゴルーチンはfdの準備完了時に透過的に駐車され、lost-wakeフリーの3状態park-commit。
Goスタイルのチャンネル — Chan(capacity), select。
スタールールとリカバリ — 予期しないブロッキングコールはハブのみをスタールさせ、ランタイムはそれを検出しリカバリする(デフォルトオン、3.13t)。
monkey.patch() はブロッキング標準ライブラリ(socket, time, threading, …)を協調的に動作させるため、既存のブロッキングコードは変更なしで実行できる。
既にasync defコードがありますか? runloom.aioブリッジはそれをシングルスレッドスケジューラで実行します(runloom.aio.run(main()) ≈ asyncio.run) — マルチコア高速化ではなく、ゼロリライトのポートパスです(それにはsync APIをrun(n>1, main)で使用してください)。
正直な制限
マルチコアの勝利には、フリー・スレッドCPython 3.13tが必要です(3.11+は全般的なフレームスナップショット)。GILビルドではRunloomは依然として動作します — 安価なスポーン、ゴルーチンモデル、netpoll — しかし、asyncioのようにシングルコアです。
RunloomはCPythonのコアあたりの速度を向上させるものではありません。CPythonの約80k純粋Python ops/s/coreという定数は変更できません。それは1つのプロセスがブロッキングモデルで各コアで同時にその速度に到達することを可能にします。
スケジューラ自体はGoクラスです。
Goよりもゴルーチンあたりのメモリが多い(空のファイバーで約3.3倍 — CPython evalフレーム;Cハンドラはほとんどのメモリを閉じます)。
プリエンプションはPythonバイトコード境界でのみ発生します — タイトな純粋Cコール(例:numpy)内のゴルーチンは、戻るまでハブを保持します(Go + cgoと同じ)。
Linux x86_64 / 3.13tがプライマリで、高度に検証されたターゲットです(2M接続実行、ファジング、サニタイザー、形式モデル);他のバックエンドはステップインで維持されていますが、それほど深く実行されていません。
プラットフォームサポート
OS / アーキ
ネットポール テスト済み
Linux x86_64
fcontext-asm
epoll
はい — hw, 3.11 / 3.12 / 3.13t / 3.14t (プライマリ)
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 / 他のBSD
ucontext / asm
select / epoll / kqueue
レビュー
ドキュメントとレイアウト
ドキュメント/にある完全なガイド:クイックスタート · Asyncioブリッジ · Sync API · チャンネル · M:N並列性 · クックブック · APIリファレンス
ディレクトリの内容
src/runloom_c/
C拡張:スケジューラ、チャンネル、ネットポール、アセンブリバックエンド、M:Nハブ、スタールリカバリ
src/runloom/
Pythonレイヤー:aio, sync, monkey, time, runtime
tests/ · examples/ · benchmark/ · docs/
テスト · 実行可能な例 · ベンチマーク + パフォーマンスハーネス · ドキュメント
ソースからのビルド(貢献者):
pip install -e . クローンから(Cコンパイラが必要;scripts/install.sh / scripts\install.bat でブートストラップ)。
フリー・スレッドCPythonに対してRunloomをハックするには、3.13tインタープリタを使用してください。