Goroutines in Rust
개요
Rust 언어에서 Go의 M:N 스케줄러를 충실히 이식한 go-lib 라이브러리가 개발되었으며, 이는 비동기(async/await)나 런타임 없이 Go 스타일의 동시성 모델을 Rust에 구현하는 것을 목표로 합니다.
주요 내용
- Rust의 동시성 모델: Rust는 언어 자체적으로 특정 동시성 모델을 강제하지 않으며, std::thread, std::sync와 같은 기본적인 동시성 원시(primitive)와 소유권 규칙을 제공합니다. async/await, 액터 모델 등은 tokio, rayon 등 외부 라이브러리를 통해 구현됩니다.
- Go의 동시성 모델: Go는 경량 스레드인 고루틴(goroutine), 채널(channel), 그리고 스레드를 효율적으로 활용하는 워크 스틸링(work-stealing) 스케줄러를 특징으로 하는 우아한 동시성 모델을 제공합니다.
- go-lib 개발 동기: Go의 고루틴과 스케줄러 모델을 Rust에서 async/await나 tokio를 사용하지 않고 직접 이식하여 Rust에서도 Go와 유사한 동시성 프로그래밍 경험을 제공하기 위해 go-lib가 개발되었습니다.
- 개발 과정 및 Claude 활용:
* 프로젝트 초기 계획 단계에서 AI Claude에게 Go의 고루틴을 Rust로 포팅하는 계획을 요청했으나, 초기 계획은 tokio 의존성을 포함하는 등 의도와 달랐습니다.
* tokio를 제외하고 Go의 런타임 소스 코드를 직접 포팅하는 방향으로 계획을 수정했으며, G (goroutine), M (machine/thread), P (processor) 스케줄러, 채널, 뮤텍스, WaitGroup 등을 Rust로 구현하는 단계별 계획이 수립되었습니다.
* 개발 과정은 AI Claude에게 각 단계를 순차적으로 지시하고, 예상치 못한 문제 발생 시 계획을 수정하며 진행되었습니다.
* AI Claude는 Go의 어셈블리 코드를 Rust와 Rust 어셈블리로 성공적으로 포팅했으며, doc comments, doc tests, unit tests, integration tests를 포함했습니다.
* 개발 중 AI 세션 토큰 제한, 테스트의 불안정성(flakiness) 문제, 미묘한 행(hang) 및 패닉(panic) 디버깅 등이 있었습니다.
- go-lib 기능 및 개선:
* go_lib::run(|| { ... }) 함수로 고루틴을 실행하는 방식에서 #[go_lib::run] 속성으로 변경되어 사용 편의성이 증대되었습니다.
* std::thread::scope와 유사한 go_lib::scope(|s| { ... }) 기능이 추가되어 스코프 내에서 고루틴을 안전하게 사용할 수 있게 되었습니다.
* 초기 수만 개의 고루틴 생성 시 발생했던 문제를 해결하기 위해 many_goroutines 테스트를 통해 workers 설정을 조정하고, 고루틴 스택 크기를 2KB로 유지하는 등의 노력이 있었습니다.
- 현재 상태 (v0.5.0): go-lib v0.5.0은 Go 스타일의 동시성을 Rust에서 async 없이 구현한 작동 가능한 라이브러리입니다. Ubuntu x86-64, macOS AArch64, Windows x86-64에서 CI를 통과하며, SIGURG를 통한 비동기 선점(preemption) 기능을 포함합니다. 75,000개의 동시 고루틴에서도 올바르게 작동하며, 스택 성장, 고루틴, 채널, select, scope, 워크 스틸링 스케줄러, 선점 기능이 정상 작동합니다.
시사점
go-lib는 Rust에서 Go의 효율적인 M:N 스케줄러와 고루틴 모델을 구현함으로써, async/await 기반의 동시성 프로그래밍과는 다른 대안을 제시하며, 특히 고성능 서버 애플리케이션 개발에 새로운 가능성을 열어줄 수 있습니다.
댓글
GitHub Discussions