내일배움캠프/TIL

[내배캠][Unity6기][TIL] 11.15 최종 프로젝트 시작 전, 알아두면 좋을 것들 특강(스탠다드) (2)

binary는 호남선 2024. 12. 3. 22:28

Preview

주제: 성능 저하를 일으키는 요인

- 사용하지 않는 생명 주기 함수
- Find 계열 함수
- Debug.Log
- LINQ
- 순서를 고려하지 않은 연산식
- Camera.main
- 문자열 덧셈
- 반복문에서 new WaitForSeconds
- ObjectPool 미사용
- 빈번한 Boxing & Unboxing


사용하지 않는 생명 주기 함수

- 일단 생명 주기 함수가 등록되면 내부에 코드가 없어도 매 프레임마다 호출되므로 리소스 낭비

- 사용하지 않는 생명 주기 함수는 반드시 삭제 필요

Find 계열 함수

- 메모리에 있는 모든 게임오브젝트와 컴포넌트에 대해 검사하기 때문에 자주 호출될수록 성능 부하 큼

- 빌드 단계의 코드에서 최대한 제거 필요

 

참고 자료: https://docs.unity3d.com/kr/2022.3/Manual/BestPracticeUnderstandingPerformanceInUnity7.html

Debug.Log

- 빌드에도 디버그 로그가 포함되어 보안에 취약

- 빌드에서 제거되지 않고 실행되므로 리소스 낭비

- 필요하다면 Debug.LogAssertion() 사용, 빌드 시에 제거됨 (간혹 제거되지 않는 경우 발생)

- 에디터에서만 동작하고 빌드 시에는 제거되는 클래스 직접 구현하여 사용(가장 권장되는 방법)

LINQ

- 동일한 로직을 LINQ가 아닌 for 나 if로 대체할 수 있음

- 동일한 로직을 for나 if로 구현하면 성능 향상, 데이터 커지고 반복 횟수 증가할수록 차이 증가

- .NET Framework 상위 버전에서는 LINQ 성능 최적화되어 크게 차이나지 않지만 Unity에서는 아직 4.8(구버전) 사용하므로 성능 좋지 않음

참고 자료: https://medium.com/swlh/is-using-linq-in-c-bad-for-performance-318a1e71a732

순서를 고려하지 않은 연산식

- 연산 횟수, 피연산자의 타입을 고려하여 효율적인 연산식 작성 필요

ex) Vector3 연산

private Vector3 dir;    // (1, 1, 1)
private float speed = 2;

// Time.deltaTime을 3이라고 가정(원래는 약 0.0027)
private void Start()
{
    // 1번
    // Vector3(2, 2, 2) 3번 => (6, 6, 6) => 6번
    transform.position += dir * speed * Time.deltaTime;
    // 2번
    // (2*3) 1번 => (6, 6, 6) => 4번
    transform.position += speed * Time.deltaTime * dir;
}

Camera.main

- GameObject 중 MainCamera로 태그된 오브젝트를 가져옴

- Update에서 사용 시 매번 카메라를 찾아오므로 그전에 미리 캐싱하여 사용하기

private Camera cam;

private void Start()
{
    cam = Camera.main;  // Good
}

private void Update()
{
    Camera.main...; // Bad
}

문자열 덧셈

- + 연산자로 문자열을 덧셈하여 사용 시 메모리 재할당하며 리소스 낭비 & 메모리 파편화

- 새로운 문자열을 할당할 공간을 찾고 재할당, 이전 문자열 공간은 참조 해제하며 GC 작동하며 이중으로 리소스 사용

- 문자열 보간이나 StringBuilder 사용 권장

반복문에서 new WaitForSeconds

- Coroutine 호출 시 반복문에서 new WaitforSeconds 사용하면 매번 새로운 객체를 생성

- 캐싱하여 사용하면 성능 향상, GC 호출 횟수 감소

private WaitForSeconds waitSeconds = new WaitForSeconds(1f);	// 캐싱

IEnumerator co()
{
    while (true)
    {
        yield return new WaitForSeconds(1); // Bad
        yield return waitSeconds; // Good
    }
}

ObjectPool 미사용

- 자주 생성되고 파괴되는 오브젝트들을 objectpool로 관리하지 않으면 성능 저하 매우 큼

- 설계상 오브젝트 풀에 없을 때 추가로 생성하고 반환하는 경우 부모오브젝트 아래 자식으로 오브젝트 풀 생성하지 않기

-- 오브젝트 풀로 새로 생성된 객체를 계층에서 부모 오브젝트의 자식으로 배치하려면 트리 구조 변경 필요하므로 오히려 성능 저하, 그냥 최상위 오브젝트로 계속 생성되게 설계하기

- 오브젝트 풀 내 제한된 개수로만 운용하는 경우면 자식오브젝트로 묶어서 관리해도 OK

빈번한 Boxing & Unboxing

- Boxing:  값 -> 참조

- Unboxing: 참조 -> 값 (참조가 해제되므로 GC 호출)

- 불필요한 boxing & unboxing 사용 지양