Runloom: 무료 스레드 Python 3.13t+용 Go 스타일 코루틴
Runloom: 무료 스레드 Python 3.13t+용 Go 스타일 코루틴
Runloom은 Go 스타일의 스택 풀 코루틴(섬유)을 구현한 고성능 Python 런타임으로, 개발자가 차단형 코드를 작성하면서 단일 프로세스 내 모든 사용 가능한 CPU 코어에 걸쳐 확장할 수 있게 합니다. 무료 스레드 CPython(3.13t 및 3.14t)을 활용함으로써 Runloom은 전역 인터프리터 락(GIL) 병목을 제거하고, 작업 스틸링 스케줄러가 수백만 개의 섬유를 여러 네이티브 스레드에 분배하도록 합니다.
고성능 M:N 스케줄링
Runloom은 많은 섬유(M)를 더 적은 수의 네이티브 허브 스레드(N) 위에 매핑하는 M:N 작업 스틸링 스케줄러를 구현합니다. 이 아키텍처를 통해 Python 애플리케이션은 OS 스레드의 오버헤드나 async/await 구문의 복잡성 없이도 대규모 동시성을 달성할 수 있습니다.
핵심 기술 요소는 다음과 같습니다:
- 핸드롤 어셈블리 컨텍스트 스위칭: x86_64 SysV와 aarch64 어셈블리를 사용해 컨텍스트 교환을 수행(~80 ns/스위치)하며, 시스템 콜 없이 Windows Fibers 또는 POSIX
ucontext에 대한 폴백을 제공합니다. - 작업 스틸링 아키텍처: 허브당 하나의 Chase‑Lev deque와 허브별 MPSC 제출을 이용해 코어 간 부하를 균형 있게 배분합니다.
- PyThreadState 스냅샷: 각 섬유에 대해 CPython eval 프레임, 데이터 스택, 예외 정보 및 컨텍스트 변수를 캡처해 수백만 개의 섬유가 yielded될 때 발생하는 "프레임 체인 절벽"을 방지합니다.
- Netpoll 통합: 파일 디스크립터 준비 상태에 따라 섬유를 투명하게 대기시키는 크로스 플랫폼 폴링 시스템(epoll, kqueue, IOCP, WSAPoll, select)을 제공합니다.
- 정체 격리: 런타임은 특정 허브 스레드를 정체시키는 예기치 않은 차단 호출을 감지하고 복구할 수 있어, 시스템의 나머지 부분이 계속 응답하도록 합니다.
Go와의 성능 벤치마크
무료 스레드 CPython 3.13t를 사용한 64코어 머신에서 수행된 벤치마크에서 Runloom은 여러 핵심 영역에서 Go와 동등한 성능을 보이지만, 메모리 사용량이 더 높습니다.
| Metric | Runloom | Go | Verdict |
|---|---|---|---|
| Pure C Spawn | 2.29 M/s | 2.10 M/s | Beats Go |
| Python Spawn | 1.35 M/s | 2.10 M/s | 0.65× |
| Context Switch | ~75ns yield / ~560ns chan | ~50ns Gosched |
Parity |
| Conn/s (Churn) | ~75–78 k/s | ~75–78 k/s | Parity |
| Req/s (Keep-alive) | 596 k/s | 603 k/s | 0.99× (Parity) |
| Memory (Empty Fiber) | 8.8 KB | 2.7 KB | 3.3× Gap |
Runloom이 처리량과 스케줄링 속도에서 Go와 일치하지만, 주요 트레이드오프는 메모리입니다. 중단된 섬유는 CPython eval 프레임 요구사항 때문에 Go 고루틴보다 약 3.3배 더 많은 메모리를 필요로 합니다.
개발자 경험 및 API
Runloom은 동기 API와 표준 라이브러리 차단 호출을 협력적으로 만드는 몽키 패치 시스템을 제공함으로써 async/await 사용을 피할 수 있게 합니다.
동기 섬유 API
개발자는 runloom.fiber(fn)으로 섬유를 생성하고 Go 스타일 채널(Chan, select)을 통해 통신할 수 있습니다.
import threading, runloom
from urllib.request import urlopen
runloom.monkey.patch()
def crawl(url):
# Monkey-patching makes this blocking call cooperative
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) # Runs on 8 hub threads across real cores
Asyncio 브리지
기존 비동기 코드를 위해 runloom.aio는 단일 스레드 스케줄러에서 async def 함수를 실행할 수 있는 브리지를 제공하여 전체 코드베이스를 완전히 재작성하지 않고도 마이그레이션 경로를 제공합니다.
제한 사항 및 요구 사항
멀티코어 병렬성을 달성하려면 무료 스레드 CPython 3.13t 또는 3.14t가 필요합니다. 표준 GIL 활성 빌드에서도 Runloom은 저비용 섬유 생성 및 netpoll을 제공하지만, asyncio와 마찬가지로 단일 CPU 코어에 제한됩니다.
다른 제약 조건은 다음과 같습니다:
- 바이트코드 선점: 선점은 Python 바이트코드 경계에서만 발생합니다. 순수 C 확장(예: NumPy)에서 긴 루프를 실행하는 섬유는 C 호출이 반환될 때까지 허브 스레드를 점유합니다.
- 메모리 오버헤드: 벤치마크에서 언급된 바와 같이 섬유는 Go 고루틴보다 더 많은 메모리를 사용합니다.
- 플랫폼 검증: Linux, macOS, Windows, FreeBSD 전반에 걸쳐 지원되지만, Linux x86_64에서 3.13t가 가장 주요하고 검증이 가장 많이 된 대상입니다.
커뮤니티 관점
개발자 커뮤니티의 초기 반응은 기술적 성취와 새로운 복잡한 런타임에 대한 신중함을 동시에 강조합니다. 일부 사용자는 프로젝트 규모가 12,000줄 이상의 C 코드와 240,000줄 이상의 Python 코드로 구성되어 단일 커밋에 도입하기엔 상당히 방대하다고 지적하며 초기 신뢰성과 안정성에 대한 의문을 제기했습니다. 다른 이들은 gevent와 비교해 GIL 제거로 가능해진 M:N 작업 스틸링 모델이 이전 협력 멀티태스킹 라이브러리보다 큰 진보라고 평가했습니다.
요약: Runloom은 무료 스레드 Python 3.13t+에 M:N 작업 스틸링 스케줄링과 스택 풀 코루틴을 도입하여 차단형 코드를 GIL 없이 여러 CPU 코어에 걸쳐 확장할 수 있게 합니다.
제목: Runloom: 무료 스레드 Python 3.13t+용 Go 스타일 코루틴