Version: 2019.4
리소스 폴더
특별 최적화

일반 최적화

여러 가지 이유로 성능 문제가 발생하는 것처럼 코드 최적화도 다양한 방법으로 수행할 수 있습니다. 개발자들은 CPU 최적화를 적용하기 전에 항상 애플리케이션을 면밀히 검토해야 합니다. 하지만 보편적으로 적용 가능한 간단한 CPU 최적화도 있습니다.

ID별 주소 프로퍼티

Unity에서는 내부적으로 문자열 이름을 사용하여 애니메이터, 머티리얼 및 셰이더 프로퍼티를 지정하지 않습니다. 속도 개선을 위해 모든 프로퍼티 이름은 프로퍼티 ID로 해시되며, 이 ID가 실제로 프로퍼티를 지정하는 데 사용됩니다.

따라서 애니메이터, 머티리얼이나 셰이더에서 Set 이나 Get 메서드를 사용할 때는 항상 문자열 값 메서드 대신 정수값 메서드를 이용합니다. 문자열 방식은 간단히 문자열 해싱을 수행한 후 해시된 ID를 정수값 메서드로 전달합니다.

문자열 해시에서 생성된 프로퍼티 ID는 단일 실행 과정에서 결정적인 값을 가집니다. 프로퍼티 ID를 사용하는 가장 간단한 방법은 각 프로퍼티 이름마다 정수 변수를 선언하여 해당 스트링 자리에 정수 변수를 사용하는 것입니다. 이는 시작 시 자동으로 초기화되며 추가적인 초기화가 필요하지 않습니다.

애니메이터 프로퍼티 이름에 적합한 API는 Animator.StringToHash이고, 머티리얼 및 셰이더 프로퍼티 이름에 적합한 API는 Shader.PropertyToID입니다.

비할당 물리 API 사용

Unity 5.3 이상 버전에서 모든 물리 쿼리 API의 비할당 버전이 도입되었습니다. RaycastAll 호출을 RaycastNonAlloc으로 대체하고, SphereCastAll 호출은 SphereCastNonAlloc으로 대체합니다. 2D 애플리케이션의 경우에도 모든 Physics 2D 쿼리 API의 비할당 버전이 제공됩니다.

UnityEngine.Object 서브 클래스에 대한 null 비교

Mono 및 IL2CPP 런타임은 UnityEngine.Object에서 파생되는 클래스의 인스턴스를 특정한 방식으로 처리합니다. 인스턴스에서 메서드를 호출하면 실제로는 엔진 코드로 호출되기 때문에 룩업 및 확인을 수행하여 스크립트 레퍼런스를 네이티브 레퍼런스로 전환해야 합니다. 크기는 작지만 이 유형의 변수를 null과 비교하는 것은 순수 C# 변수에 대해 비교하는 것보다 훨씬 더 많은 성능이 소모됩니다. 따라서 빠른 루프에서 또는 프레임마다 실행되는 코드에서는 null 비교를 가급적 사용하지 마십시오.

벡터와 쿼터니언 연산 및 연산 순서

빠르게 반복하는 루프에 위치한 벡터 및 쿼터니언 연산의 경우 부동 소수점보다 정수 연산의 속도가 빠르며, 부동 소수점 연산의 속도가 벡터, 매트릭스 또는 쿼터니언 연산보다 빠르다는 점을 기억하십시오.

따라서 가환 연산 또는 결합 연산이 허용될 경우 개별 수학 연산에 드는 성능 소모를 최소화할 수 있습니다.


Vector3 x;

int a, b;

// Less efficient: results in two vector multiplications

Vector3 slow = a * x * b;

// More efficient: one integer mult, one vector mult

Vector3 fast = a * b * x;

빌트인 ColorUtility

HTML 형식의 컬러 문자열(#RRGGBBAA)과 Unity의 네이티브 ColorColor32 구조체 간에 전환해야 하는 애플리케이션의 경우 Unify Community의 스크립트를 사용하는 것이 일반적입니다. 이 스크립트는 문자열 조작으로 인해 속도가 느리고 메모리 사용량이 컸습니다.

Unity 5에서는 이러한 전환을 효율적으로 수행하는 빌트인 ColorUtility API가 제공됩니다. 이 빌트인 API를 사용하는 것이 좋습니다.

Find 및 FindObjectOfType

출시 단계의 코드에서 Object.FindObject.FindObjectOfType의 사용을 모두 제거하는 것이 일반적으로 가장 좋습니다. 이러한 API는 Unity가 메모리에 있는 모든 게임 오브젝트와 컴포넌트에 대해 반복해서 수행하기 때문에 프로젝트의 범위가 커질수록 빠르게 성능이 저하됩니다.

싱글톤 오브젝트의 접근자에 대해 위의 법칙에 대한 예외를 만들 수 있습니다. 전역 관리자 오브젝트는 보통 “인스턴스” 프로퍼티를 노출하며, 기존에 존재하는 싱글톤 인스턴스를 감지하기 위해 보통 게터 안에 FindObjectOfType 호출이 있습니다.


class SomeSingleton {

    private SomeSingleton _instance;

    public SomeSingleton Instance {

        get {

            if(_instance == null) { 

                _instance =

                    FindObjectOfType<SomeSingleton>(); 

            }

            if(_instnace == null) { 

                _instance = CreateSomeSingleton();

            }

            return _instance;

        }

    }

}

이 패턴은 일반적으로 허용되지만, 싱글톤 오브젝트가 존재하지 않는 씬에서 이 접근자가 호출되는지 코드를 확인하는 것이 중요합니다. 게터가 누락된 싱글톤 인스턴스를 자동으로 생성하지 않을 경우 싱글톤을 찾는 코드가 FindObjectOfType을 반복적으로 프레임당 여러 번 호출하게 되어 바람직하지 않은 성능 저하가 자주 발생합니다.

카메라 로케이터

내부적으로 Camera.main 프로퍼티는 Object.FindObject의 특화된 배리언트인 Object.FindObjectWithTag를 호출합니다. 이 프로퍼티에 액세스하는 것은 Object.FindObjectOfType을 호출하는 것보다 비효율적입니다. 코드가 반드시 메인 카메라를 지정해야 하는 경우 다음 방법 중 하나를 수행하십시오.

  • Start 또는 OnEnable 콜백에서 Camera.main에 액세스하고 그에 따른 레퍼런스 값을 저장합니다.

  • 액티브 카메라에 레퍼런스 값을 제공하거나 삽입할 수 있는 Camera Manager 클래스를 구성합니다.

디버그 코드 및 [Conditional] 속성

UnityEngine.Debug 로깅 API는 개발 모드가 아닌 빌드에서도 제거되지 않으며, 호출될 경우 로그 파일에 기록합니다. 대부분의 개발자가 개발 모드가 아닌 빌드에는 디버그 정보를 기록하지 않으므로 다음과 같이 사용자 정의 메서드에 개발 모드 전용 로깅 호출로 래핑하는 것이 좋습니다.


    public static class Logger {

            [Conditional("ENABLE_LOGS")]

            public static void Debug(string logMsg) {

                UnityEngine.Debug.Log(logMsg);

            }

        }

이러한 메서드를 [Conditional] 속성으로 작성하면 Conditional 속성에서 사용되는 define에 따라 컴파일된 소스에 작성된 메서드가 포함되는지 여부를 결정합니다.

Conditional 속성에 전달된 define이 하나도 정의되지 않은 경우 작성된 메서드와 작성된 메서드의 모든 호출이 컴파일을 통해 제외됩. 이 효과는 해당 메서드와 그 메서드의 모든 호출이 #if … #endif 전처리기 블록에 둘러싸였을 때와 동일합니다.

Conditional 속성에 관한 더 자세한 내용은 MSDN 웹사이트를 참조하십시오. msdn.microsoft.com

리소스 폴더
특별 최적화