전투 시스템을 구현하는데 처음에는 ACharacter를 상속받은 주인공 Character 대상으로 공격 판정을 하다가,
또 다른 ACharacter를 상속받은 Monster가 공격 판정 대상에 추가되었으며, 최근에는 AActor를 상속받은 Weapon까지 공격 판정에 추가되었다.
그러다보니 공통되는 기능을 통합하여 코드 양을 줄임에 따라 상속과 캐스팅을 할 일이 많아졌는데,
한가지 핵심 기능을 갖는 함수 하나를 중복 코드 없이 통합할 수 있는 대신, 캐스팅이 1-2회 늘어나는 Trade-Off 상황이 생겼다.
내가 언리얼 엔진 C++을 처음 배울 때만 해도 Cast<>는 비싼 연산이고, 가능하다면 최대한 줄이거나 최초 1회 실행시 가능한 캐싱하여 쓰도록 배웠는데, 이참에 캐스팅에 대해 Forum, Reddit, Unreal Source 등 각 커뮤니티를 돌며 알아봤다.
우선 결론만 말하자면, 언리얼 C++의 UObject를 대상으로하는 Cast가 비싸다고 하는 것은 널리 알려진 미신이며 매 Tick()당 수천번 실행되는게 아닌이상 성능적으로 Bottleneck이 될 수 없다.
몇몇 숙련자들은 Cast가 심지어 FVector의 size() 보다 비싸지 않으니 안심하라고 말한다.
처음 Casting 했을 때 캐싱 해둔다면 성능에 대해 걱정할 필요가 없다.
[ 많은사람들이 Casting이 비싸다고 생각해온 이유 ]
우선 언리얼 Cast<>()의 엔진 코드를 들여다 보면, "Dynamically cast an object type-safely." 라는 주석이 등장한다.
초심자 입장에선 native C++의 RTTI를 활용하는 Dynamic_cast를 래핑한 기능이라고 생각해 비싸다고 느낄 것이다.
[ Time Complexity of Cast<> ]
Cast는 내부적으로 DoCast를 실행하며, Docast는 Src가 어떤 타입인지 판정하기 위해 내부에서 IsA<>()를 실행하고 있다.
IsA 내부에서 가장 핵심적인 기능은 IsChildOf 인데, Reflection 정보를 이용하여 상속 구조내에서 loop를 돌기 때문에
Editor 환경에서 O(Depth(InheritanceTree))의 Linear 시간이 소요된다.
Editor가 아닐 경우 O(1) 의 비용을 가지는 IsChildOfUsingStructArray()를 통해 IsChildOf 여부를 확인한다.
[ 요약 ]
Cast<T>는 내부적으로 Reflection을 이용해 UObject을 대상으로 type-safe한 형 변환을 수행하며, 실패시 nullptr을 반환한다.
에디터 환경에서 O(Depth(InheritanceTree)) 만큼의 선형 시간이 소요되며,
에디터가 아닌 런타임 환경에서 O(1)의 상수 시간이 소요된다.
프로그래머 입장에서 처음 캐스팅시 Caching 해두고 사용한다면 성능측면에선 저렴한 축에 속하며, 비싸다는 오해가 널리 알려져 있다.
[ Reference ]
https://forums.unrealengine.com/t/how-much-casting-is-expensive/442279/3
'UE5' 카테고리의 다른 글
[UE5] UE_LOG, FName (0) | 2024.07.25 |
---|---|
[UE5] Visibility for DragDropOperation (0) | 2024.07.25 |
애니메이션 리타겟팅 루트모션관련 에러 (0) | 2024.07.12 |
[UE5] Package Build에 Asset 경로 포함 (0) | 2024.07.03 |
[UE5] UWidgetAnimation (0) | 2024.04.06 |