Go의 스케줄러는 OS 개입 없이 갈증을 방지하기 위해 협동 및 선점 멀티태스킹 모델을 혼합하여 사용합니다. 1.14 버전 이후 런타임은 타임 슬라이스(일반적으로 10ms)를 초과하는 고루틴을 실행하는 스레드에 SIGURG 신호를 전송하여 비동기 선점 지점을 주입합니다. 신호 처리기가 고루틴이 함수 호출을 하거나 스택에 접근하기 직전에 안전한 지점을 감지하면 스케줄러는 컨텍스트를 저장하고 다른 실행 가능한 고루틴으로 전환합니다. 이 메커니즘은 함수 호출 없이도 타이트한 CPU 바운드 루프가 프로세서를 무한히 독점할 수 없도록 보장합니다.
우리의 고빈도 거래 플랫폼은 시장 변동 중에 치명적인 지연 스파이크를 경험했습니다. 단일 분석 고루틴이 복잡한 몬테카를로 시뮬레이션을 수행하면서 오더 처리 파이프라인이 수백 밀리초 동안 정지되었습니다. 이 문제는 고루틴이 함수 호출 없이 타이트한 수학 루프를 실행하여 1.14 이전의 스케줄러가 이를 선점할 수 없도록 했던 것을 시사합니다.
우리는 이 경합을 해결하기 위해 세 가지 다른 접근 방법을 평가했습니다. 첫 번째 옵션은 시뮬레이션 루프 내에 runtime.Gosched() 호출을 수동으로 삽입하는 것이었습니다. 이 접근 방식은 즉각적인 완화를 제공했지만 상당한 유지 관리 오버헤드를 초래했고 개발자가 깊은 스케줄러 지식을 요구하여 리팩토링 시 문제가 발생할 수 있는 취약한 코드를 만들어냈습니다.
두 번째 솔루션은 분석 작업 부하를 별도의 마이크로서비스로 분리하여 CPU 제한을 두는 것이었습니다. 이 방법은 하드 격리 및 독립적인 확장을 제공했지만 네트워크 직렬화 오버헤드와 프로세스 간 통신의 추가 지연이 위험 계산에 대한 서브 밀리초 지연 요구를 위반했습니다.
궁극적으로 우리는 런타임을 Go 1.20으로 업그레이드하고 GOMAXPROCS를 물리적 CPU 코어에 맞도록 명시적으로 조정하기로 선택했습니다. 이 업그레이드는 신호를 통한 비동기 선점을 제공하여 스케줄러가 코드 수정 없이 매 10ms마다 CPU 바운드 고루틴에 강제로 양보할 수 있게 했습니다. 배포 후 메트릭은 피크 부하 중 P99 지연이 8ms에서 안정화되어 타임아웃 폭주를 제거하고 단일 프로세스 아키텍처의 단순성을 유지했습니다.
함수 호출이 없는 타이트한 루프가 이전 Go 버전에서 스케줄링 문제를 일으키는 이유는 무엇입니까?
Go 1.14 이전에는 스케줄러가 협동적 선점에만 의존했기 때문에 고루틴은 함수 호출, 통신 작업 또는 뮤텍스 경합에서만 자발적으로 양보했습니다. 순수 산술 연산을 수행하는 타이트한 루프는 안전한 지점에 도달하지 않아 완료될 때까지 자신의 **Processor (P)**를 독점했습니다. 현대 Go는 비동기 선점을 사용하여 스레드에 SIGURG 신호를 보내고 함수 호출 여부에 관계없이 다음 안전한 지점에서 컨텍스트 전환을 트리거합니다.
**스케줄러는 **Processor (P)가 사용 가능할 때 어떤 고루틴이 다음에 실행될지를 어떻게 결정합니까?
스케줄러는 먼저 현재 P의 로컬 실행 대기열을 확인한 후 무작위 시작 인덱스를 사용하여 다른 P의 로컬 대기열에서 고루틴의 절반을 빼가려는 작업 도용 알고리즘을 구현합니다. 로컬 대기열이 비어 있으면 새로운 고루틴의 갈증 방지를 위해 61개의 스케줄러 틱마다 전역 실행 대기열을 확인합니다. 이러한 계층적 선택은 동기화 비용을 최소화하고 모든 사용 가능한 Machine (M) 스레드 간의 로드 밸런싱을 보장합니다.
**고루틴이 파일 I/O와 같은 블로킹 시스템 호출을 실행할 때 **Processor (P)에는 어떤 일이 발생합니까?
고루틴이 시스템 호출에서 차단되면 Go 런타임은 즉시 Machine (M) 스레드를 해당 P에서 분리하고 해당 P를 새로운 또는 대기 중인 M에 할당하여 다른 고루틴들이 같은 OS 스레드 추상화에서 계속 실행될 수 있도록 합니다. 원래의 M은 시스템 호출에 들어가고 커널이 작업을 완료할 때까지 대기합니다. 반환 후에는 원래의 P를 다시 획득하려 시도하거나 P가 이제 다른 스레드에 바인드되어 있으면 자신을 주차합니다. 이 M:N 멀티플렉싱은 I/O 중 OS 스레드가 유휴 상태가 되는 것을 방지하고 수천 개의 고루틴에서 높은 CPU 사용률을 유지합니다.