返回

文章详情

展示 HN: Runloom – Python 的 Go 风格协程支持多线程

Hacker News2026年7月10日 13:07

针对 Python 的 Go 风格有栈协程。编写阻塞代码——fiber(fn)、普通的 recv/send,无 async/await——并在一个进程中的每个核心上运行一百万个协程。自定义汇编上下文切换 + C 任务偷窃调度器 + 网络轮询,为自由线程的 Python 3.14t (关闭 GIL) 构建。import threading,from urllib.request import urlopen runloom.monkey.patch() def crawl(url): # urlopen() 看起来是阻塞的——但 monkey.patch() 会将协程停放在 # 套接字上而不是操作系统线程上,因此所有 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 和 Go 同样的环境 (64c, 自由线程 CPython 3.13t),8 个中心 / GOMAXPROCS=8,热稳态。 Go ≈ 2.1 M spawn/s。 指标 runloom Go 律判 spawn — 纯 C (c_entry) 2.29 M/s 2.10 M/s 胜过 Go spawn — Python (runloom.fiber) 1.35 M/s 2.10 M/s 0.65× 上下文切换 ~75 ns yield · ~560 ns chan RT ~50 ns Gosched ~平衡 conn/s — churn (每个请求的新连接) ~75–78 k/s ~75–78 k/s 平衡 req/s — keep-alive echo, Python 处理程序 596 k/s 603 k/s 0.99× — 平衡 (C 处理器胜过 Go) 内存 — 空的停放 fiber 8.8 KB 2.7 KB 3.3× (唯一的大差距) 简短故事:在 spawn、调度和吞吐量方面,runloom 给予 Go 以竞争,并在原始 spawn 上胜过 Go——一个栈式协程运行时在 CPython 中与编译语言匹配,甚至在 Python 处理程序中(在饱和时,596 k 对 603 k req/s;C 处理程序胜过 Go)。 唯一真实的差距是内存:一个挂起的 fiber 携带 CPython eval 框架,约为 Go 的每个 fiber 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 适用于 Linux(x86_64/aarch64)、macOS(arm64/x86_64)、Windows(AMD64)的 CPython 3.11–3.14 的预构建 wheel(不需要编译器);其他地方源代码构建。没有运行时依赖。 它是什么 自定义汇编上下文切换 (x86_64 SysV, aarch64) — ~80 ns/swap,无系统调用;Windows Fibers / POSIX ucontext 备份。M:N 任务偷窃调度器 (3.13t) — 每个中心的 Chase-Lev 双端队列,每个中心的 MPSC 提交,唤醒的协程路由回其源中心。每个协程的 PyThreadState 快照——cframe,datastack,exc_info,contextvars,递归;一百万个被暂停的协程共享它们的中心线程,没有帧链悬崖。 netpoll — epoll / kqueue / IOCP / WSAPoll / select;协程在 fd 准备就绪时透明地停放,失落唤醒无阻塞的三态停放-提交。 Go 风格的通道——Chan(capacity),select,for v in ch。停滞隔离 + 恢复——一个意外的阻塞调用只会停滞其中心,运行时会检测 + 恢复它(默认开启,3.13t)。 monkey.patch() 使阻塞 stdlib (socket,time,threading,…) 成为合作的,因此现有的阻塞代码保持不变。已有 async def 代码?runloom.aio 桥接在单线程调度器上运行它(runloom.aio.run(main()) ≈ asyncio.run)——一个零重写的移植路径,而不是多核心加速(对于此目的使用同步 API,run(n>1,main))。 诚实的限制 多核心的胜利需要自由线程的 CPython 3.13t (3.11+ 才能获得帧快照)。 在 GIL 构建中 runloom 仍然可以运行——便宜的生成,协程模型,netpoll——但是像 asyncio 一样是单核心。 runloom 不能使每个核心的 Python 运行得更快。CPython 的 ~80 k 纯 Python ops/s/core 是一个常数,它无法提升;它允许一个进程在每个核心上同时达到这个值,采用阻塞模型。调度器本身是 Go 水平的。每个协程的内存比 Go 更高(~3.3× 对于一个空的 fiber——CPython eval 框架;C 处理程序关闭了大部分内存)。抢占仅在 Python 字节码边界触发——在一个紧密的纯 C 调用(例如 numpy)内的协程会在返回之前保持其中心(与 Go + cgo 相同)。 Linux x86_64 / 3.13t 是主要、经过严格验证的目标(2 M-conn 运行,模糊测试,消毒器,正式模型);其他后端保持同步,但测试较少。 平台支持 OS / arch 切换 netpoll 测试的 Linux x86_64 fcontext-asm epoll 是 —— 硬件,3.11 / 3.12 / 3.13t / 3.14t(主要) Linux aarch64 fcontext-asm epoll qemu macOS x86_64 / arm64 fcontext-asm kqueue 硬件,3.14t FreeBSD / GhostBSD fcontext-asm kqueue 硬件,3.12 Windows 10/11 / Server 2022 Fibers IOCP→WSAPoll→select 硬件,3.14t Solaris / Android / 其他 BSD ucontext / asm select / epoll / kqueue 查看 文档 & 布局 docs/中的完整指南:快速入门 · Asyncio 桥 · 同步 API · 通道 · M:N 并行性 · 食谱 · API 参考 目录 内容 src/runloom_c/ C 扩展:调度器,通道,netpoll,asm 后端,M:N 中心,停滞恢复 src/runloom/ Python 层:aio

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡