← 返回信息流
AI 资讯Hacker News·3 小时前

Runloom:为Python自由线程引入Go风格协程

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

速览

Runloom是一个新项目,为Python的自由线程环境提供Go语言风格的协程支持。它允许开发者在Python中高效地管理并发任务,类似Go的goroutine。该项目旨在提高Python在并发编程中的性能和易用性。

AI 深度解读

背景

Python 的全局解释器锁(GIL)长期限制多线程并发性能。CPython 3.13 引入了自由线程模式(free-threaded,即 -X gil=0 或 3.13t 构建),允许真正的并行执行。然而,Python 的标准异步模型(async/await)基于协作式协程,虽能高效处理 I/O,但难以利用多核;而传统的 threading 模型又因 GIL(自由线程下 GIL 关闭)和线程切换开销,不适合大规模并发。Go 语言以其轻量级 goroutine 和 M:N 调度器闻名,能在单进程内调度数百万协程。Runloom 项目旨在为 Python 的自由线程环境提供 Go 风格的栈式协程(stackful coroutines)运行时,让开发者用同步阻塞风格写代码,却获得接近 Go 的并发性能。

核心内容

Runloom 是一个面向 Python 自由线程模式(3.14t、3.13t)的栈式协程库,完全模仿 Go 的 goroutine 模型。其核心设计包括:

  • 栈式协程(fiber):不同于 async/await 的栈式(stackless)协程,Runloom 的 fiber 拥有自己的调用栈,可以挂起任意深度的函数调用,支持 fiber(fn) 创建协程,通过 recv/send 通信,无需 async/await 关键字。
  • 手写汇编上下文切换:针对 x86_64 SysV 和 aarch64 架构,实现了约 80 ns/次的上下文切换,无系统调用;Windows 上回退到 Windows Fibers,其他平台使用 POSIX ucontext
  • M:N 工作窃取调度器:使用 Chase-Lev 双端队列 per hub,每个 hub 有独立的 MPSC 提交队列;唤醒的 goroutine 被路由回其原始 hub,实现负载均衡。
  • 每个 goroutine 独立保存 CPython 状态:包括 cframedatastackexc_infocontextvarsrecursion 等,使得百万级挂起协程共享 hub 线程的帧链而不会导致栈溢出。
  • 网络轮询(netpoll):基于 epoll(Linux)、kqueue(macOS)、IOCP(Windows)、WSAPoll、select,让 goroutine 在等待 I/O 时透明地挂起,且实现无丢失唤醒的三态 park-commit 机制。
  • Go 风格通道Chan(capacity)selectfor v in ch 等。
  • 阻塞隔离与恢复:当某个 goroutine 意外进行阻塞调用(如 C 扩展),该调用只会阻塞其所在的 hub 线程,运行时能检测并恢复该 hub,默认开启(仅 3.13t 支持)。
  • 猴子补丁(monkey.patch()):将标准库中 sockettimethreading 等阻塞调用改为协作式,使得现有同步阻塞代码无需改动即可运行在 Runloom 上。
  • asyncio 桥接:通过 runloom.aio 模块,已有的 async def 代码可以在单线程调度器上运行(相当于 asyncio.run),但这不是多核加速方案;真正的多核并行需使用同步 API 配合 run(n>1, main)

性能对比(64核、自由线程 CPython 3.13t,8个hub vs Go GOMAXPROCS=8,稳态):

  • 纯 Python 处理程序(handler):Runloom ≈ 596k req/s,Go ≈ 603k req/s,基本持平。
  • C 处理程序:Runloom 可超越 Go。
  • 原始创建吞吐量:Runloom 约 2.1M spawn/s,优于 Go。
  • 内存:每个空 fiber 的 RSS 约为 Go 的 3.3 倍,主要来自 CPython 的 eval frame。

配置选项:runloom.optimize("throughput") 使用最大创建速率模式(fiber_fast),runloom.optimize("memory") 使用紧凑栈(默认)。

安装:pip install runloom,提供预编译 wheel(无编译器需求),支持 CPython 3.11–3.14 在 Linux、macOS、Windows 上运行,无运行时依赖。其他平台需从源码编译。

关键要点

  • Runloom 是 Python 自由线程模式下首个 Go 风格栈式协程运行时,利用手写汇编和 M:N 工作窃取调度器实现高性能并发。
  • 开发者可以用同步阻塞风格(fiberrecv/send)编写代码,通过猴子补丁自动将阻塞 I/O 转为协作式,无需学习 async/await
  • 在纯 Python 处理程序场景下,Runloom 的吞吐量与 Go 持平(596k vs 603k req/s),原始协程创建速度甚至超越 Go(2.1M/s)。
  • 主要劣势是内存开销:每个挂起 fiber 约是 Go goroutine 的 3.3 倍。但若使用 C 处理程序,差距可大幅缩小。
  • 调度器达到工业级:优先在 Linux x86_64 + 3.13t 上经过 200 万连接测试、模糊测试、形式化模型验证;其他后端(macOS、Windows)保持同步维护但未深度验证。
  • 局限:CPython 单核纯 Python 执行速度约 80k ops/s,这是 Python 本身的瓶颈,Runloom 无法提升单核性能,只是让多核都跑满这一速度;抢占仅在 Python 字节码边界触发,若 goroutine 陷入纯 C 调用(如 numpy)会阻塞所在 hub 直到返回(与 Go + cgo 相同)。
  • 适用于需要高并发、高吞吐、多核并行、且希望保持同步编码风格的项目,尤其适合已有大量阻塞 I/O 代码的 Python 应用迁移到自由线程模式。

意义与影响

  • 填补了 Python 在高并发并行编程上的空白:传统 asyncio 单线程单核,multiprocessing 进程间通信开销大,threading 在自由线程之前受 GIL 限制。Runloom 首次在 CPython 上实现了堪比编译语言的协程调度效率,且与自由线程 CPython 深度整合。
  • 降低了并发编程门槛:无需学习复杂的异步语法,用熟悉的 urlopentime.sleep 等阻塞函数即可获得高并发,通过 monkey.patch() 无缝适配,降低了现有代码迁移成本。
  • 为 Python 在服务端领域(如 Web 服务器、微服务、爬虫框架)提供了与 Go 竞争的可能性。虽然纯 Python 性能仍有差距,但 C 扩展场景下表现优异,且生态兼容性优于 Go(可直接利用 Python 丰富库)。
  • 推动 CPython 自由线程模式的实用化:Runloom 证明了在 Python 中实现真正多核并发的可行性,为 CPython 核心团队和社区提供了宝贵的参考。未来若 CPython 进一步优化 eval frame 内存,Runloom 的内存差距可能缩小。
  • 开源生态影响:提供全平台 wheel、无依赖安装,降低了使用门槛;完善文档(快速入门、asyncio 桥接、同步 API、通道、M:N 并行、Cookbook、API 参考),便于学习和扩展。开发者还可以通过 pip install -e . 从源码构建并贡献代码。
查看原文 →github.com