일반 최적화
Apply broadly useful CPU optimizations, such as addressing properties by ID, to improve runtime performance.
읽는 시간 3분최근 업데이트: 한 달 전
코드를 최적화하는 방법은 성능 문제의 원인만큼 다양합니다. 일반적으로 개발자는 CPU 최적화를 적용하기 전에 애플리케이션을 세밀하게 프로파일링하는 것이 좋습니다. 그러나 범용으로 적용할 수 있는 몇 가지 간단한 CPU 최적화가 있습니다.
ID별 주소 프로퍼티
Unity는 문자열 이름을 사용하여 Animator, Material, Shader 프로퍼티를 내부적으로 지정하지 않습니다. 속도 측면에서 모든 프로퍼티 이름은 프로퍼티 ID로 해시되며, 이러한 ID는 실제로 프로퍼티를 다루는 데 사용됩니다.
따라서 애니메이터, 머티리얼 또는 셰이더에서 Set 또는 Get 메서드를 사용할 때는 문자열 값 메서드 대신 정수 값 메서드를 사용합니다. 문자열 메서드는 단순히 문자열 해싱을 수행한 후 해시된 ID를 정수 값 메서드로 전달합니다.
문자열 해시로 생성된 프로퍼티 ID는 단일 실행 과정에서 결정적입니다. 이를 사용하는 가장 간단한 방법은 각 프로퍼티 이름에 대해 정적 읽기 전용 정수 변수를 선언하고 문자열 대신 정수 변수를 사용하는 것입니다. 이는 시작 시 추가 초기화 코드 없이 자동으로 초기화됩니다.
Animator 프로퍼티 이름에 적합한 API는 Animator.StringToHash이고, Material 및 Shader 프로퍼티 이름에 적합한 API는 Shader.PropertyToID입니다.
비할당 물리 API 사용
Unity 5.3 이상에는 모든 물리 쿼리 API의 비할당 버전이 도입되었습니다. RaycastAll 호출을 RaycastNonAlloc으로, SphereCastAll 호출을 SphereCastNonAlloc으로 대체합니다. 2D 애플리케이션에는 할당이 불가능한 모든 Physics2D 쿼리 API 버전도 있습니다.
UnityEngine.Object 서브 클래스에 대한 null 비교
Mono 및 IL2CPP 런타임은 특정 방식으로 UnityEngine.Object에서 파생된 클래스의 인스턴스를 처리합니다. 인스턴스에서 메서드를 호출하면 실제로 엔진 코드가 호출되며, 엔진 코드는 스크립트 레퍼런스를 네이티브 레퍼런스로 전환하기 위해 조회와 확인을 수행해야 합니다. 이 유형의 변수를 null과 비교하는 비용은 작지만, 순수한 C# 클래스에 비해 훨씬 더 많이 사용됩니다. 따라서 자주 실행되는 루프나 프레임마다 실행되는 코드에서는 이러한 null 비교를 피해야 합니다.
벡터와 쿼터니언 연산 및 연산 순서
빠르게 반복하는 루프에 위치한 벡터 및 쿼터니언 연산의 경우 부동 소수점보다 정수 연산의 속도가 빠르며, 부동 소수점 연산의 속도가 벡터, 매트릭스 또는 쿼터니언 연산보다 빠르다는 점을 기억하십시오.
따라서 가환 연산 또는 결합 연산이 허용될 경우 개별 수학 연산에 드는 성능 소모를 최소화할 수 있습니다.
Vector3 x;int a, b;// Less efficient: results in two vector multiplicationsVector3 slow = a * x * b;// More efficient: one integer mult, one vector multVector3 fast = a * b * x;
빌트인 ColorUtility
일반적으로 HTML 포맷의 컬러 문자열()과 Unity의 네이티브 및 구조 간에 전환해야 하는 애플리케이션은 Unify 커뮤니티의 스크립트를 사용합니다. 이 스크립트는 문자열 조작으로 인해 느리고 광범위한 메모리 할당이 발생했습니다.
#RRGGBBAAColorColor32Unity 5부터는 이러한 전환을 효율적으로 수행하는 ColorUtility API가 빌트인되어 있습니다. 이러한 빌트인 API를 사용하는 것이 좋습니다.
Find 및 FindObjectOfType
정식 제작 코드에서 모든 및 사용을 제거하는 것이 일반적인 베스트 프랙티스입니다. 이러한 API는 Unity가 메모리의 모든 게임 오브젝트와 컴포넌트에 대해 반복 작업(iteration)을 수행해야 하므로 프로젝트의 범위가 커질수록 성능이 저하됩니다.
GameObject.FindObject.FindObjectOfType싱글톤 오브젝트의 액세서에서 위 규칙에 대한 예외를 만들 수 있습니다. 전역 관리자 오브젝트는 '인스턴스' 프로퍼티를 노출하는 경우가 많으며, 싱글톤의 기존 인스턴스를 감지하기 위해 getter에 종종 호출이 있는 경우가 많습니다.
FindObjectOfTypeclass SomeSingleton { private SomeSingleton _instance; public SomeSingleton Instance { get { if(_instance == null) { _instance = FindObjectOfType<SomeSingleton>(); } if(_instance == null) { _instance = CreateSomeSingleton(); } return _instance; } }}
이 패턴은 일반적으로 허용되지만, 코드를 검사하고 싱글톤 오브젝트가 없는 씬에서 액세서가 호출되었는지 확인하는 것이 중요합니다. getter가 누락된 싱글톤의 인스턴스를 자동으로 생성하지 않는 경우, 싱글톤을 찾는 코드가 에 반복적으로 호출(프레임당 여러 번)되어 성능이 바람직하지 않게 저하되는 경우가 매우 흔합니다.
FindObjectOfType디버그 코드 및 [Conditional] 속성
UnityEngine.Debugpublic static class Logger { [Conditional("ENABLE_LOGS")] public static void Debug(string logMsg) { UnityEngine.Debug.Log(logMsg); } }
이러한 메서드를 [Conditional] 속성으로 작성하면 Conditional 속성에서 사용되는 define에 따라 컴파일된 소스에 작성된 메서드가 포함되는지 여부를 결정합니다.
Conditional 속성에 전달된 정의가 하나도 정의되지 않으면 decorated 메서드 및 decorated 메서드에 대한 모든 호출이 컴파일됩니다. 이 효과는 메서드와 메서드에 대한 모든 호출이 프리 프로세서 블록에 래핑된 경우와 동일합니다.
#if … #endifConditional