PART 8 - 강의 4/6

Task Graph System

의존성 기반 작업 스케줄링으로 복잡한 병렬 처리 구현

01

Task Graph란?

의존성 기반 작업 시스템

Task Graph는 작업 간의 의존성을 정의하여 자동으로 실행 순서를 결정합니다. 선행 작업이 완료되면 후속 작업이 자동으로 스케줄링됩니다.

Task A
Task B
Task C

Task A와 Task B가 완료되면 Task C가 자동 실행

02

커스텀 Task 정의

Task 클래스 구현

C++
#include "Async/TaskGraphInterfaces.h" // 커스텀 Task 클래스 class FMyComputeTask { public: FMyComputeTask(int32 InData, TArray<float>* InResults, int32 InIndex) : Data(InData) , Results(InResults) , Index(InIndex) { } // Task 이름 (디버깅용) static const TCHAR* GetTaskName() { return TEXT("FMyComputeTask"); } // 프로파일링용 Stat ID FORCEINLINE static TStatId GetStatId() { RETURN_QUICK_DECLARE_CYCLE_STAT(FMyComputeTask, STATGROUP_TaskGraphTasks); } // 실행할 스레드 static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyThread; } // 후속 작업 추적 모드 static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; } // 실제 작업 수행 void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { // 무거운 계산 수행 (*Results)[Index] = ExpensiveCalculation(Data); } private: int32 Data; TArray<float>* Results; int32 Index; float ExpensiveCalculation(int32 Input) { // 복잡한 계산... return FMath::Sqrt((float)Input); } };
03

Task 생성 및 실행

TGraphTask 사용법

C++
// 독립 Task 생성 및 실행 FGraphEventRef Task1 = TGraphTask<FMyComputeTask>::CreateTask() .ConstructAndDispatchWhenReady(100, &Results, 0); FGraphEventRef Task2 = TGraphTask<FMyComputeTask>::CreateTask() .ConstructAndDispatchWhenReady(200, &Results, 1); // 의존성 있는 Task (Task1, Task2 완료 후 실행) FGraphEventArray Prerequisites; Prerequisites.Add(Task1); Prerequisites.Add(Task2); FGraphEventRef Task3 = TGraphTask<FMyComputeTask>::CreateTask(&Prerequisites) .ConstructAndDispatchWhenReady(300, &Results, 2); // 완료 대기 (필요시) FTaskGraphInterface::Get().WaitUntilTaskCompletes(Task3);

복수 Task 대기

C++
// 여러 Task 동시에 대기 TArray<FGraphEventRef> AllTasks; AllTasks.Add(Task1); AllTasks.Add(Task2); AllTasks.Add(Task3); // 모든 Task 완료 대기 FTaskGraphInterface::Get().WaitUntilTasksComplete(AllTasks);
04

실전 예제: 파이프라인

다단계 처리 파이프라인

C++
// 데이터 처리 파이프라인 // Stage 1: 데이터 로드 (병렬) // Stage 2: 데이터 처리 (Stage 1 완료 후) // Stage 3: 결과 병합 (Stage 2 완료 후) void UDataProcessor::ProcessDataPipeline() { // Stage 1: 데이터 로드 Tasks TArray<FGraphEventRef> LoadTasks; for (int32 i = 0; i < DataSources.Num(); i++) { FGraphEventRef LoadTask = TGraphTask<FLoadDataTask>::CreateTask() .ConstructAndDispatchWhenReady(DataSources[i], &LoadedData, i); LoadTasks.Add(LoadTask); } // Stage 2: 처리 Tasks (모든 로드 완료 후) TArray<FGraphEventRef> ProcessTasks; for (int32 i = 0; i < LoadedData.Num(); i++) { // 해당 데이터의 로드 완료 대기 FGraphEventArray Prereqs; Prereqs.Add(LoadTasks[i]); FGraphEventRef ProcessTask = TGraphTask<FProcessDataTask>::CreateTask(&Prereqs) .ConstructAndDispatchWhenReady(&LoadedData[i], &ProcessedData, i); ProcessTasks.Add(ProcessTask); } // Stage 3: 병합 Task (모든 처리 완료 후) FGraphEventRef MergeTask = TGraphTask<FMergeResultsTask>::CreateTask(&ProcessTasks) .ConstructAndDispatchWhenReady(&ProcessedData, &FinalResult); // 완료 대기 FTaskGraphInterface::Get().WaitUntilTaskCompletes(MergeTask); // Game Thread에서 결과 사용 OnProcessingComplete(FinalResult); }
처리 파이프라인
Load 1
Load 2
Load 3
Process 1
Process 2
Process 3
Merge
05

GetDesiredThread 옵션

Task 실행 스레드 지정

C++
// 어느 스레드에서든 실행 (기본) static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyThread; } // Game Thread에서 실행 (UObject 접근 필요 시) static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::GameThread; } // 백그라운드 일반 우선순위 static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyBackgroundThreadNormalTask; } // 백그라운드 높은 우선순위 static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyBackgroundHiPriTask; }
SUMMARY

핵심 요약

핵심 포인트
  • Task Graph - 의존성 기반 작업 스케줄링 시스템
  • FGraphEventRef - Task 완료 이벤트 핸들
  • TGraphTask - Task 생성 및 디스패치
  • Prerequisites - 선행 Task 의존성 정의
  • WaitUntilTaskCompletes - Task 완료 대기
  • GetDesiredThread - 실행 스레드 지정
AsyncTask vs Task Graph

단순 비동기 작업은 AsyncTask, 복잡한 의존성이 있는 파이프라인은 Task Graph를 사용하세요.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Task Graph 기본 태스크 생성

FGraphEventRef와 FFunctionGraphTask::CreateAndDispatchWhenReady()를 사용하여 간단한 태스크를 스케줄링하세요. 태스크 완료 대기(FTaskGraphInterface::WaitUntilTaskCompletes)를 구현하세요.

실습 2: 태스크 의존성 체인 구축

여러 태스크를 의존성 관계로 연결하세요. 예: 맵 데이터 로드 -> AI 초기화 -> NPC 배치 순서로 실행되도록 Prerequisites 배열을 설정하세요.

심화 과제: RPG 로딩 파이프라인 구축

Task Graph를 활용하여 RPG 레벨 로딩을 파이프라인화하세요. 지형 생성, 에셋 로딩, NPC 스폰, 퀘스트 초기화를 병렬 가능한 부분은 병렬로, 의존성이 있는 부분은 순차적으로 실행하세요.