icon안동민 개발노트

메모리 및 CPU 사용량 최적화


 언리얼 엔진 게임의 메모리 사용량과 CPU 부하를 최적화하는 것은 원활한 게임 경험을 제공하는 데 핵심적입니다.

 이 절에서는 다양한 최적화 기법과 도구를 살펴보겠습니다.

메모리 프로파일링 도구 사용법

 언리얼 메모리 프로파일러 활용

#include "ProfilingDebugging/MemoryStats.h"
 
void UMemoryProfiler::StartMemoryProfiling()
{
    FMemoryStats::Initialize();
    FMemoryStats::EnableStatsTracking(true);
}
 
void UMemoryProfiler::StopMemoryProfiling()
{
    FMemoryStats::EnableStatsTracking(false);
    FMemoryStats::DumpStats(TEXT("MemoryProfile.csv"));
}

메모리 누수 탐지 및 해결 방법

 메모리 누수 디버거 구현

UCLASS()
class UMemoryLeakDetector : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Memory")
    void StartLeakDetection();
 
    UFUNCTION(BlueprintCallable, Category = "Memory")
    void StopLeakDetection();
 
private:
    TMap<UObject*, int32> ObjectReferences;
 
    void TrackObject(UObject* Object);
    void UntrackObject(UObject* Object);
};
 
void UMemoryLeakDetector::StartLeakDetection()
{
    ObjectReferences.Empty();
    FCoreUObjectDelegates::OnConstructedObject.AddUObject(this, &UMemoryLeakDetector::TrackObject);
    FCoreUObjectDelegates::OnDestroyedObject.AddUObject(this, &UMemoryLeakDetector::UntrackObject);
}

효율적인 메모리 관리 기법

 오브젝트 풀링 시스템 구현

UCLASS()
class UObjectPool : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Memory")
    AActor* GetPooledObject(TSubclassOf<AActor> ObjectClass);
 
    UFUNCTION(BlueprintCallable, Category = "Memory")
    void ReturnObjectToPool(AActor* Object);
 
private:
    TMap<TSubclassOf<AActor>, TArray<AActor*>> PooledObjects;
};
 
AActor* UObjectPool::GetPooledObject(TSubclassOf<AActor> ObjectClass)
{
    if (PooledObjects.Contains(ObjectClass) && PooledObjects[ObjectClass].Num() > 0)
    {
        return PooledObjects[ObjectClass].Pop();
    }
    return GetWorld()->SpawnActor<AActor>(ObjectClass);
}

CPU 병목 현상 식별

 CPU 프로파일러 사용

#include "ProfilingDebugging/CpuProfilerTrace.h"
 
void UCPUProfiler::StartCPUProfiling()
{
    FCpuProfilerTrace::StartCpuProfiling();
}
 
void UCPUProfiler::StopCPUProfiling()
{
    FCpuProfilerTrace::StopCpuProfiling();
    FCpuProfilerTrace::DumpCpuProfile(TEXT("CPUProfile.csv"));
}

멀티스레딩 최적화 전략

 비동기 작업 관리자 구현

UCLASS()
class UAsyncTaskManager : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Optimization")
    void ExecuteAsyncTask(TFunction<void()> Task);
 
private:
    FCriticalSection TaskLock;
    TArray<TFunction<void()>> TaskQueue;
    FRunnableThread* WorkerThread;
 
    void ProcessTasks();
};
 
void UAsyncTaskManager::ExecuteAsyncTask(TFunction<void()> Task)
{
    FScopeLock Lock(&TaskLock);
    TaskQueue.Add(MoveTemp(Task));
}

게임 로직 및 AI 시스템의 효율적인 구현

 행동 트리 최적화

UCLASS()
class UOptimizedBehaviorTree : public UBehaviorTree
{
    GENERATED_BODY()
 
public:
    virtual void TickTree(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
 
private:
    void UpdateHighPriorityNodes(UBehaviorTreeComponent& OwnerComp, float DeltaSeconds);
    void UpdateLowPriorityNodes(UBehaviorTreeComponent& OwnerComp, float DeltaSeconds);
};
 
void UOptimizedBehaviorTree::TickTree(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
    UpdateHighPriorityNodes(OwnerComp, DeltaSeconds);
 
    static float LowPriorityUpdateInterval = 0.5f;
    static float TimeSinceLastLowPriorityUpdate = 0.0f;
    TimeSinceLastLowPriorityUpdate += DeltaSeconds;
 
    if (TimeSinceLastLowPriorityUpdate >= LowPriorityUpdateInterval)
    {
        UpdateLowPriorityNodes(OwnerComp, TimeSinceLastLowPriorityUpdate);
        TimeSinceLastLowPriorityUpdate = 0.0f;
    }
}

오픈 월드에서 리소스 관리 전략

 레벨 스트리밍 관리자 구현

UCLASS()
class ULevelStreamManager : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Optimization")
    void UpdateStreamingLevels(FVector PlayerLocation);
 
private:
    TArray<ULevelStreaming*> ActiveStreamingLevels;
    void LoadNearbyLevels(FVector Location);
    void UnloadDistantLevels(FVector Location);
};
 
void ULevelStreamManager::UpdateStreamingLevels(FVector PlayerLocation)
{
    LoadNearbyLevels(PlayerLocation);
    UnloadDistantLevels(PlayerLocation);
}

가비지 컬렉션 최적화

 가비지 컬렉션 관리자 구현

UCLASS()
class UGarbageCollectionManager : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Optimization")
    void ScheduleIncrementalGC();
 
private:
    FTimerHandle GCTimerHandle;
    void PerformIncrementalGC();
};
 
void UGarbageCollectionManager::ScheduleIncrementalGC()
{
    GetWorld()->GetTimerManager().SetTimer(GCTimerHandle, this, &UGarbageCollectionManager::PerformIncrementalGC, 1.0f, true);
}
 
void UGarbageCollectionManager::PerformIncrementalGC()
{
    GEngine->ForceGarbageCollection(true);
}

에셋 로딩 및 스트리밍 개선 방법

 비동기 에셋 로더 구현

UCLASS()
class UAsyncAssetLoader : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Optimization")
    void LoadAssetAsync(TSoftObjectPtr<UObject> AssetToLoad, FOnAssetLoaded OnLoaded);
 
private:
    TQueue<TPair<TSoftObjectPtr<UObject>, FOnAssetLoaded>> LoadQueue;
    void ProcessLoadQueue();
};
 
void UAsyncAssetLoader::LoadAssetAsync(TSoftObjectPtr<UObject> AssetToLoad, FOnAssetLoaded OnLoaded)
{
    LoadQueue.Enqueue(TPair<TSoftObjectPtr<UObject>, FOnAssetLoaded>(AssetToLoad, OnLoaded));
    ProcessLoadQueue();
}

메모리 및 CPU 최적화가 미치는 영향

 1. 부드러운 프레임레이트

  • 효율적인 CPU 사용으로 안정적인 FPS 유지
  • 메모리 관리 최적화로 프레임 지연 현상 감소

 2. 로딩 시간 단축

  • 효율적인 에셋 로딩 및 스트리밍으로 게임 시작 및 레벨 전환 시간 감소
  • 메모리 사용 최적화로 빠른 데이터 접근 가능

 3. 반응성 향상

  • CPU 병목 현상 해소로 게임 로직 및 입력 처리 속도 개선
  • 메모리 단편화 감소로 인한 시스템 전반적인 성능 향상

 4. 안정성 증가

  • 메모리 누수 방지로 장시간 플레이 시에도 안정적인 성능 유지
  • 효율적인 리소스 관리로 크래시 및 프리징 현상 감소

다양한 하드웨어의 최적화 접근 방식

 1. 스케일러블 시스템 설계

  • 하드웨어 성능에 따라 동적으로 조정되는 LOD 시스템 구현
  • 저사양 디바이스를 위한 경량화된 알고리즘 및 에셋 준비

 2. 플랫폼별 최적화

  • 모바일 플랫폼을 위한 메모리 사용량 최소화 전략 수립
  • 콘솔 플랫폼의 특화된 하드웨어 기능 활용

 3. 성능 프로파일 기반 최적화

  • 다양한 하드웨어 구성에서의 성능 데이터 수집 및 분석
  • 병목 지점 식별 및 타겟 플랫폼별 맞춤형 최적화 적용

 메모리 및 CPU 사용량 최적화는 게임의 전반적인 성능과 사용자 경험을 크게 향상시킬 수 있는 핵심적인 과정입니다.

 메모리 프로파일링 도구와 CPU 프로파일러를 효과적으로 활용하면 성능 병목 지점을 정확히 식별하고 해결할 수 있습니다.

 메모리 누수 탐지 및 해결은 장기적인 게임 안정성을 위해 매우 중요합니다.

 오브젝트 풀링과 같은 효율적인 메모리 관리 기법을 통해 메모리 할당 및 해제 오버헤드를 크게 줄일 수 있습니다.

 CPU 최적화에 있어 멀티스레딩 전략은 현대 멀티코어 프로세서의 성능을 최대한 활용할 수 있게 해줍니다.

 비동기 작업 관리자를 구현하여 병렬 처리를 효과적으로 활용할 수 있습니다.

 게임 로직 및 AI 시스템의 효율적인 구현은 CPU 부하를 줄이는 데 중요한 역할을 합니다.

 행동 트리의 최적화된 구현은 복잡한 AI 로직을 효율적으로 처리할 수 있게 해줍니다.

 대규모 오픈 월드 게임에서는 레벨 스트리밍과 에셋 로딩 최적화가 특히 중요합니다.

 플레이어 위치에 따른 동적 레벨 로딩/언로딩 전략은 메모리 사용을 효율적으로 관리할 수 있게 해줍니다.

 가비지 컬렉션 최적화는 게임의 안정성과 반응성을 크게 향상시킬 수 있습니다.

 증분식 가비지 컬렉션 구현은 긴 프레임 지연을 방지하는 데 도움이 됩니다.

 에셋 로딩 및 스트리밍 개선은 게임의 로딩 시간을 단축시키고 전반적인 성능을 향상시킵니다.

 비동기 에셋 로딩 시스템은 게임 플레이 중 심리스한 콘텐츠 로딩을 가능케 합니다.