2024. 3. 8. 21:03ㆍ내일배움캠프
3월 8일
요즘 부쩍 최적화에 대한 관심이 많아졌다.
그동안 아무렇지 않게 싸질러놓은 코드들을 보면 내가 만든 게임은 왜이렇게 프레임이 떨어지는건지
뭐가 문제인건지 몰랐는데
이번 기회에 자주 사용하는 최적화 기법은 어떤게 있는지 코드를 짤때 어떤 형식으로 짜야
부담이 덜 되는지를 알아보도록하자
최적화
최적화의 지표라고 할 수 있다.
FPS : 1초에 화면이 얼마나 다시 그려지느냐의 단위이다.
게임을 좋아하고 자주 한다면 아 '프레임 떨어져' 라는 말을 한번쯤은 들어봤을껀데 바로 그 프레임이다
FPS와 같이 주의깊게 볼건 바로 Batches이다
Baches는 Cpu가 GPU에게 어떠한 물체를 그리라고 요청하는 것을 말한다.
이미지에선 346이라고 적혀져있으니깐 346개의 물체를 그려달라고 요청하고 있다는 뜻!!
모바일의 경우 100개가 상한선이기 때문에 합칠수 있는건 최대한 합쳐가지고 Scene위에 올리는게 좋다.
FPS는 올리고!! Batch를 줄이는게 관건이라 할 수 있다.
자 그러면 해당 프로젝트에서 어떤 녀석이 메모리를 많이 잡아먹고 렉을 유발하는 범인인지
어떻게 알아볼까?
바로 프로파일링이다
어디서 많이 본거같은 그래프가 잔뜩 그려진 창이 하나가 열리는데
- Deep Profile
- 체크 시 모든 스크립트가 프로파일링 됨
- 체크안함
- 체크함
체크하는게 더 자세한 정보를 들여다 볼 수 있어서 범인을 정확하게 찾아낼 수 있다.
일단 위 이미지를 보면 GameObject.Find()라는 녀석이 보인다.
일단 Find()는 사용자가 굉장히 편하게 GameObject를 찾아낼수 있게 해준다.
하지만 머리가 편하면? 컴퓨터가 고생한다....
유니티에서도 이 기능의 주의할 점을 알려주고 있는데
헉 진짜 하지 말아야겠다.. 왜 성능상 이유로 매 프레임마다 기능을 사용하는걸 권장하지 않는 것인가....
게임의 성능 저하 요소인 GC 가비지 컬렉터 되시겠다
사실 가비지 컬렉터는 굉장히 편한 기능중 하나이다. 미사용 힙 메모리를 자동으로 파악하고 해제하는
프로세스이기 때문에 GC가 없다면 우린 C/C++ 처럼 메모리를 직접할당해주고 해제하고 하는 번거로운 작업을
해야한다.... 하지만 이 역시나 몸이 편하면 컴퓨터가 고생하는 법 잦은 GC의 방문은 성능을 저하시키는 큰 요소이며
Find 나 FindwitTag 그리고 GetComponent의 반복적인 사용은
주소 체게를 사용하는 참조형(string, Class 등등)은 힙 영역에 할당하게 되고 GC의 잦은 방문을 초래하게 되는
성능 저하의 원인중 하나이다. (성인 사망률 1위 느낌)
또한 빈 Unity 이벤트 함수는 삭제해주는게 좋다
Start, Update는 우리가 Unity에서 스크립트를 하나 만들면 자동적으로 스크립트에 들어가 있는 녀석이다.
하지만 Start나 Update문에 아무것도 적어두지 않았더라도 선언되어 있다면 내부 각 이벤트 컨테이너에 추가되고
이벤트 발생 시 매번 호출되기 때문에 오버헤드가 발생한다. 그러니 Unity 이벤트 함수는 꼭 필요한 것만 남겨두고
필요하지 않다면 다 지워주도록 하자.
그 다음으로 주의해야할께 박싱과 언박싱이다. 박싱과 언박싱의 개념은 예전 TIL에도 적어둔것 같지만
최적화 관점에서 박싱과 언박싱은 지양해야 되는 기법 중 하나이다. 왜냐?
박싱 과정을 보자 박싱은 힙 영역에 새로운 메모리를 할당하고 스택의 값을 힙 메모리로 복사한 뒤
힙 메모리의 주소 값을 갖는 새로운 스택 메모리를 할당하는 과정을 거친다. 이것만 해도 값 하나를 옮기는데에
굉장히 많은 메모리 참조를 한다고 느껴진다. 언박싱도 마찬가지
박싱과 언박싱은 그 자체로도 오버헤드가 있지만 GC의 방문을 초래하게 되고 GC를 동작하게 만드는
잠재적 오버헤드까지 가진 셈이다.
자 그리고 또또 주의해야할 점이 뭐가 있을까?
여기까지 정리하다보면 반복적으로 참조 형식의 변수들을 재 할당할 경우 GC의 먹이가 되고
GC의 먹이가 되는 것들이 많아질수록? 성능 저하에 원인이 된다.
그렇다면 클래스와 리스트는 어떨까?
당연하다 Update()문 내에서 클래스나 리스트를 할당하는건 굉장히 안좋은 선택이다.
마찬가지로 최근에 코루틴을 정말 많이 사용하고 있는데
코루틴 내의 new 를 반복문 내에서 사용할 경우 GC가 찾아오게 된다.
이럴땐 미리 변수를 할당받아뒀다가 사용하도록 하자
또한 무분별한 링큐 사용은 자제하자 로직을 직접 제어하지 못하기 때문에 오용하면 비효율적이고
Unity에서 사용한다면 성능저하와 함께 확실하지 않지만 iOS에서 안먹히는 버그가 있다고 한다.
또또또... 할께 많다
List 사이즈를 초기화 시켜주는게 좋다. 음 무슨 말이냐면
이렇게 사이즈를 딱 정해주지 않고 만들어진 리스트는 해당 크기가 초과된다면 자동으로 기본 크기의 두배로 할당한다. 리스트의 장점이자 단점인데
내가 딱 하나만 더 넣고 싶다면?
이렇게 미리 사이즈가 5인 리스트로 만들어주면 재할당하지 않고 크기가 5인 List가 만들어진다
물론 크기가 줄었다 늘었다 하는 녀석이라면 내가 안에 들어갈 값이 얼만큼 큰 녀석인지 특정하기 힘들다면 어쩔수없지만
이런 부분에서 짜잘하게나마 성능을 가져갈 수 있다는점은 기억해 둘만 하다.
또또또
string 연산은 줄이는게 좋다 string의 += 연산은 객체 할당이 일어나기 때문에 연속으로 사용시 GC가 찾아온다
문자열이 자주 변경될 상황이라면 StringBuilder를 사용해보자!!
또또또
클래스와 구조체이다 클래스는 참조타입이며 구조체는 값 타입이라는걸 이제는 안다.
몇 개 안되는 변수들로 이루어진 간단한 타입은 클래스가 아닌 구조체로 만들어서 불필요한 힙 할당을 줄이도록!!
또또또
에셋 임포트에 관련된 내용이다
POT 텍스쳐를 사용하자!! Pot란 가로/세로 크기가 2의 승수(1,2,4,8,16,32....)가 되는 텍스쳐들을 말한다
POT가 아닌 텍스쳐들은 가장 가까운 사이즈의 2의 승수를 찾아서 텍스쳐를 복사하기 때문에
메모리에 2개분량의 텍스쳐가 올라가게 된다고 한다. 상대적으로 메모리통이 큰 PC 게임에서라면 감당할 수 있지만
내가 만들고 있는 게임이 모바일 게임이라면? 어우 당장 지금 사용하고 있는 텍스쳐 크기를 확인해보러 달려가자
또또또
Sprite Atlas를 활용하자
유니티의 ProjectSetting - Editor - Mode에서 설정해주고
이렇게 만들어준다
아틀라스는 여러 텍스쳐를 단일 텍스쳐로 결합하는 것이다 주의할 점은 너무 많은 스프라이트를 집어넣으면
아틀라스 크기 자체가 커지기 때문에 적절하게 만들어주는게 뽀인트라고 할 수 있다.
아틀라스에 이미지를 묶어두지 않은 채로 저렇게 많은 이미지를 올려두게된다면 Bathces의 수가 올라가게된다.
아까도 말했듯이 FPS는 올리고 Batch는 줄여야하는데 너무 많은걸...?
하지만 아틀라스로 묶어둔 이미지들을 올리면 Batches가 확 줄어든 모습을 볼 수 있다.
보통 UI 팝업별로 혹은 씬별로 스프라이트 아틀라스를 만들어서 사용해서 아틀라스 크기 그리고 드로우콜의 적절한
균형이 이루어진다고 한다.
분량조절 실패.... 라이팅 기법과 카메라로 최적화 하는 방법은 내일 이어서 정리해보도록 하자....
3월 8일 TIL은 여기서 마치도록 하겠다
'내일배움캠프' 카테고리의 다른 글
240312-TIL (0) | 2024.03.12 |
---|---|
240311-TIL (0) | 2024.03.11 |
240307-TIL (0) | 2024.03.07 |
240306-TIL (0) | 2024.03.06 |
240305-TIL (1) | 2024.03.05 |