피드로 돌아가기
Dev.toBackend
원문 읽기
Go 런타임이 P Handoff와 Work Stealing 메커니즘으로 수백만 고루틴을 소수의 OS 쓰레드로 효율적으로 스케줄링하는 구조를 공개했다.
Inside the Go Scheduler: How GMP Model Powers Millions of Goroutines
AI 요약
Context
OS 쓰레드는 각 클라이언트 요청마다 생성되어 시스템 리소스를 과도하게消耗한다. 단일 글로벌 큐 방식은 락 경합으로 확장성 병목이 발생한다. 쓰레드별 로컬 큐 방식은 블로킹 시스템 콜 시 해당 쓰레드의 모든 고루틴이 대기하는 문제가 있다.
Technical Solution
- Goroutine(G)은 go 키워드로 생성되는 경량 실행 단위이며 런타임이 관리한다.
- Machine(M)은 OS 쓰레드를 추상화한 리소스이며 필요 시 생성되고 재사용된다.
- Processor(P)는 로컬 큐를 소유하는 논리적 스케줄링 컨텍스트이며 G와 M을 중계한다.
- P Handoff는 블로킹 시스템 콜 발생 시 P가 blocked M에서 분리되어 idle M에 재연결된다.
- Work Stealing은 유휴 P가 다른 P의 로컬 큐에서 절반의 고루틴을 무작위로 탈취한다.
- 61 틱 규칙은 매 61번째 스케줄링마다 글로벌 큐를 확인하여 기아 현상을 방지한다.
- Signal-based Preemption(Go 1.14+)은 sysmon이 10ms 이상 실행 중인 고루틴에 SIGURG를 전송하여 선점한다.
Impact
GOMAXPROCS를 CPU 코어 수로 설정 시 OS 컨텍스트 스위칭 비용을 절감할 수 있다. P Handoff와 쓰레드 재사용으로 쓰레드 생성 overhead를 현저히 줄인다. Per-P 로컬 큐로 글로벌 락 경합이 제거되어 확장성이 향상된다.
Key Takeaway
Go는 분산된 로컬 큐와 중앙 글로벌 큐의 하이브리드 구조로 경합과 기아 문제를 동시에 해결하며 협력적_yield와 신호 기반 선점을 조합하여 Millions 단위 고루틴을 소수의 쓰레드로 효율 운영한다.
실천 포인트
대규모 동시성 서버에서 GOMAXPROCS를 runtime.NumCPU()로 설정하고 I/O bound 작업에는 고루틴을 직접 활용하되 CPU intensive 작업은 워커 풀 패턴으로 관리하면 리소스 활용도를 극대화할 수 있다.