엔진 스케줄러와 프레임 타임 분배, 스레드 우선순위 제어, 작업 파이프라인 조율
프레임 타임 분배의 원리와 스케줄러 구조
게임 엔진은 매 프레임마다 수십 개의 작업(Task)을 수행한다. 대표적으로 입력 처리, 물리 계산, AI 업데이트, 렌더링 명령 생성, 사운드 믹싱 등이 있다. 이 모든 과정은 1/60초(16.67ms) 안에 끝나야 한다. 만약 하나라도 늦으면 프레임 드랍이 발생한다. 따라서 엔진의 스케줄러(Scheduler)는 각 작업의 우선순위와 실행 시점을 정밀하게 조정해야 한다.
스케줄러는 일반적으로 3단 구조를 가진다.
① Frame Scheduler: 프레임 단위 전체 작업 순서를 제어한다.
② Thread Scheduler: CPU 스레드 간 부하를 분산한다.
③ Job Dispatcher: 세부 태스크를 실제 코어에 분배한다.
엔진은 내부적으로 “Task Graph”를 유지한다. 이 그래프는 각 작업 간의 의존 관계를 정의하고, 순서가 필요한 작업만 직렬로, 나머지는 병렬로 수행한다. 이때 가장 중요한 기준이 Frame Time Budget이다. 예를 들어 총 16.67ms 중 물리에 3ms, 렌더링에 5ms, AI에 2ms를 할당하고, 나머지는 오버헤드와 동기화에 사용한다. 이러한 예산 개념이 없으면 특정 모듈이 과도하게 CPU를 점유하게 된다.
스레드 우선순위 제어와 동적 부하 분산
현대 게임 엔진은 멀티코어 환경을 전제로 설계된다. 8~16코어 이상 CPU에서는 스레드 간 우선순위 조정이 필수적이다. 렌더 스레드는 GPU에 커맨드를 꾸준히 공급해야 하므로, 가장 높은 우선순위를 부여받는다. 반면 물리·AI·사운드 스레드는 상대적으로 여유가 있다.
스케줄러는 실행 중인 각 스레드의 평균 실행 시간을 측정해, 프레임당 목표 시간과 비교한다. 특정 스레드가 지속적으로 초과하면, 그 우선순위를 낮추고 여유 스레드에 일부 작업을 이관한다. 이 과정을 **Dynamic Load Balancing**이라 한다. 실제로 Unreal Engine과 Unity 모두 Job System을 통해 이 방식을 사용한다.
스레드 우선순위 제어는 단순한 CPU 시간 분배를 넘어, I/O 대기와 메모리 접근 패턴까지 고려해야 한다. 예를 들어 로딩 스레드는 디스크 I/O 대기 중 CPU를 거의 사용하지 않으므로, 다른 스레드가 그 틈을 활용할 수 있다. 따라서 엔진은 스레드별 상태를 지속적으로 모니터링하고, “Ready”, “Running”, “Blocked” 상태 간 전환 빈도를 기준으로 효율을 평가한다.
이제 문제는 작업 간 동기화다. 스레드가 공유 데이터를 참조할 때, 락(lock)이나 뮤텍스(mutex)가 필요하다. 하지만 과도한 락은 프레임 타임을 불안정하게 만든다. 이를 줄이기 위해 엔진은 “락 프리(lock-free)” 구조를 사용한다. 즉, 데이터 접근 순서를 보장하되, 스핀락이나 원자적 교환(atomic exchange)으로 처리한다. 이 덕분에 스레드 간 경합이 줄고, 평균 프레임 변동폭이 5% 이하로 안정된다.
작업 파이프라인 조율과 프레임 안정화
스케줄러의 마지막 역할은 파이프라인 조율이다. 모든 작업이 독립적으로 빠르게 끝나더라도, 순서가 어긋나면 프레임이 끊긴다. 예를 들어 물리 계산이 완료되기 전에 렌더링이 시작되면, 잘못된 오브젝트 상태가 화면에 표시된다. 이를 방지하기 위해 엔진은 Barrier Synchronization을 사용한다. 각 파이프라인 단계가 완료될 때까지 다음 단계를 대기시켜 일관성을 유지한다.
하지만 완전한 동기화는 지연을 유발한다. 그래서 “Partial Barrier” 기법이 등장했다. 일부 데이터만 먼저 렌더링에 전달하고, 나머지는 다음 프레임에서 갱신한다. 이렇게 하면 평균 프레임 타임이 1~2ms 단축된다.
또한, 작업 파이프라인은 일반적으로 세 개의 큐로 구성된다.
① Compute Queue: 물리·AI·애니메이션 계산 담당.
② Render Queue: 그래픽 명령 생성 및 GPU 전송.
③ Async Queue: 스트리밍·사운드·I/O 관리.
이 세 큐가 병렬로 동작하지만, 최종 출력은 하나의 타임라인에 합쳐진다.
결국 파이프라인 조율의 목적은 “모든 코어를 사용하면서도 프레임 단위 결과를 일관되게 유지하는 것”이다.
최종적으로 엔진은 각 프레임의 실행 로그를 기록하고, CPU·GPU 사용률을 분석한다. 이를 통해 병목 구간을 자동 탐지하고, 다음 프레임에서 우선순위를 재조정한다. 이러한 반복 최적화는 스케줄러가 단순히 ‘시간 관리’가 아니라 ‘프레임 품질 관리’의 핵심임을 보여준다.