그래픽 최적화에 이어 프로그래밍, 오디오, UI 최적화에 대해 배웠다.
이렇게나 다양한 최적화 방법이 있다니 신경 쓸 부분이 정말 많은 것 같다.
프로그래밍 최적화
Garbage Collection
C++이나 Objective C와 달리 C#은 Garbage Collector에 의해 메모리 해제가 이루어진다.
값 형식과 참조 형식
값 형식은 스택에, 참조 형식은 힙에 저장된다.
힙은 가비지 컬렉터에 의해 메모리가 관리되기 떄문에 클래스 사용에 주의가 필요하다.
String
String은 참조 형식이기 때문에 주의가 필요하다.
- 연산자로 문자열을 합치면 각각의 문자열 객체가 메모리 힙에 생성되기 때문에 StringBuilder나 string.Format()을 이용한다.
- JSON, XML같이 객체가 많이 생성되는 부분을 Scriptable Object로 대체한다.
클로저
클로저에서 외부 변수를 사용하면 외부 변수가 참조된다.
그 외
- foreach문은 내부적으로 열거자 객체를 힙에 할당한다. for문 사용이 더 좋다.
- 결과 값을 배열로 반환하는 함수나 속성은 내부적으로 힙에 할당한다.
- GetComponent<t>()
- Mesh.vertices
- Camera.allCameras
- Linq는 코드를 간소화할 수 있지만 내부적으로는 복잡한 구조로 동작한다.
- 정규표현식도 복잡한 문자열을 다양하게 처리할 수 있지만 내부적으로는 복잡한 구조로 동작한다.
Throttling
발열을 줄이기 위해 70% 만 쓸 정도로 targetFrameRate를 낮추는 게 좋다.
Profiler
프로파일러 태그
특정 코드 지점을 프로파일링하기 위해 태그를 설정할 수 있다.
void SampleMethod()
{
Profiler.BeginSample("MyCode");
// Code...
Profiler.EndSample();
}
Memory Profiler
메모리 프로파일러는 패키지 매니저에서 추가로 설치해야 한다.
PC에서는 가상 메모리 시스템이 존재하기 때문에 물리적인 메모리를 초과하더라도 스토리지를 이용 해 가상 메모리를 사용하기 때문에 성능이 떨어지더라도 게임이 중단되는 상황이 발생하지 않지만, 모바일 환경에서는 PC와 동일한 가상 메모리 시스템이 존재하지 않아 물리적인 메모리를 초과하게 되면 게임이 중단된다.
Auto Sync Transform
물리엔진이 Transform 변화를 자동으로 감지하고 물리 세계에 반영할지를 결정하는 옵션이다.
Edit > Project Settings > Physics 에서 켤 수 있다.
Rigidbody가 있는 오브젝트를 물리 엔진 기반이 아닌 Transform 기반으로 위치, 회전, 스케일을 변경했을 때 물리 엔진이 바로 감지하지 못하기 때문에 이를 해결하기 위한 옵션이다.
하지만 이 옵션은 약간의 오버헤드가 발생하기 때문에 필요 없다면 옵션은 끄고, 필요한 순간에만 명령을 호출해서 동기화할 수 있다.
Physics.SyncTransform();
AddComponent() 동적 할당 주의
런타임에서 할당할 경우 성능에 영향을 미치기 때문에 사용에 주의가 필요하다.
되도록 Hierachy에서 미리 할당한다.
GetComponent() 캐싱
GetCompontnt() 함수는 실행 비용이 높기 때문에 빈번하게 사용하는 경우에는 캐싱해서 사용해야 한다.
Object Pool 패턴
빈번하게 사용되는 오브젝트는 pool 패턴을 사용한다.
빈 함수 제거
내용이 없는 Update(), Start() 함수는 제거한다.
전처리기 사용
상황에 맞는 코드를 분리한다.
\\#if UNITY_EDITOR
void Function()
{
// ...
}
\\#endif
Debug.Log() 함수 처리
최종 빌드에서는 Debug.Log()는 제거해야한다.
한 버에 비활성화하기 위해 Conditional을 사용할 수 있다.
public static class Logging
{
[System.Diagnostics.Conditional("ENABLE_LOG")]
static public void Log(object message)
{
UnityEngine.Debug.Log(message);
}
}
복잡한 계층 구조 피하기
하위 오브젝트가 있는 경우 부모 오브젝트의 Transform이 변경되면 하위 오브젝트도 Transform 연산이 발생한다.
따라서 꼭 필요한 상황에서만 계층 구조를 가지도록 한다.
Transform 변경은 한 번만 하기
하나의 오브젝트를 이동하기 위해 다수의 클래스에서 Transform을 동시에 변경하는 것은 비효율적이다.
한 프레임 내에서 Transform을 여러번 변경하는 경우에는 프레임 마지막에 한 번만 변경하도록 한다.
설정
사용하지 않는 설정은 비활성화한다.
공통
Auto Simulation 사용하지 않으면 Disable
Auto Sync Transform 사용하지 않으면 Disable
모바일
Accelerometer 사용하지 않으면 Disable
Auto Graphics API가 활성화 되어 있으면 모든 API에 대한 설정이 들어가기 때문에 필요한 API만 설정
객체 동작 처리
Occlusion Culling으로 화면에 렌더링되지 않더라도 할당된 코드는 동작하게 된다. 특정 오브젝트의 동작이 화면에 보이지 않는 상황에서 필요없다면 아래 코드로 처리한다.
void OnBecameVisible()
void OnBecameInvisible()
오디오 최적화
- 불필요한 상황에서는 mono로 설정한다.
- 샘플레이트 설정을 통해 리샘플링해서 용량을 줄일 수 있다.
- 디코딩 시 가장 적은 CPU 자원을 사용하고 재압축 과정에서 손실이 적은 WAV 포맷을 추천한다. 하지만 용량이 크기 때문에 저장공간의 여유가 있을 경우에 고려한다.
- Vorbis 압축 포맷을 사용하고, 효과음은 ADPCM을 사용한다.
- 사용하지 않는 AudioSource 컴포넌트는 제거한다.
UI 최적화
Canvas 나누기
Canvas 단위로 Draw Call을 하기 때문에 요소가 바뀔 때마다 Canvas 전체가 업데이트 된다. 따라서 정적인 UI 요소와 동적인 UI 요소를 구분해서 Canvas를 구성하면 효율적으로 처리할 수 있다.
안 보이는 UI 요소 비활성화
화면에 안 보이는 UI, 투명 패널은 화면 밖으로 이동시켜도 연산일 이루어지기 때문에 비활성화 해야한다.