목차

반응형

https://www.youtube.com/watch?v=4kVffWfmJ60

 

오늘은 네임드 개발자 오즈라엘님이 "성능 프로파일링과 최적화"라는 귀한 주제로 발표를 해주셨다. 두 시간짜리 영상을 열심히 보고 요약글을 올리는 나한테 감사하자

 

"쌍따봉 드립니다."

 

 

1.  프로파일링 유의사항

2. 유니티 프로파일러

2.1 다양한 병목현상

3. 프레임 디버거

4. 메모리 프로파일러

5. 텍스쳐 컴프레션

6. Xcode Instrument

7. 최적화 팁 & 트릭

 

 

 

 

 

 

 

 

 

 

 

 

1.  프로파일링 유의사항

타겟 플랫폼 선정

많은 팀이 간과하고 있는 것이 원래 최적화를 위해서 타겟 플랫폼과 타겟 디바이스를 정해야 한다.

무조건 좋은 퀄리티만 보여줄 수 없기 때문에 현실적으로 목표하고자 하는 디바이스와 플랫폼에 적합한 퍼포먼스를 보여줘야 한다.

사실 우리 회사도 타겟 플랫폼을 딱히 설정하고 개발을 시작하지 않는다. 하지만 가끔 게임 업계에서도 "ㅁㅁ게임은 동남아시아를 대상으로 한 게임이라서 저성능 기기를 타겟으로 개발을 한다...." 이런 말이 들려오는데 그런 환경이라면 합리적인 타겟 플랫폼 선정이 필수라고 생각이 든다.

 

최적화

최적화란 병목(다양한 원인)을 탐지하고 제거하는 반복 작업을 일컫는다.

 

위의 경우는 GPU 바운드 -> CPU는 할 일이 없지만  GPU에 할일이 많아서 렉걸림

따라서 이런 상황에서는 CPU를 최적화해도 개선이 전혀 안된다.

CPU 바운드 원인 : 로직, AI, 물리연산, 로딩, GC처리

GPU 바운드 원인 : 폴리곤, 텍스쳐, 필터링, 라이트, 프레임 버퍼, 쉐이더, 오버드로우

 

 

주기적인 최적화

프로파일링, 최적화는 개발 마지막 단계에서 하는 것이 아니라 정기적으로 하는 것이다.

그에 따른 빌드 및 QA 프로세스 정립은 필수다.

 

프로파일링 주의 사항

추측에 의한 최적화 금지

실제로 어떤 팀에서 자꾸 퍼포먼스가 안 나와서 어떤 개발자가 텍스쳐가 문제인 것 같아서 텍스쳐를 전부 하향평준화해달라고 아티스트한테 요청해서 적용했는데 해결이 안 됐다. 굉장히 비효율적인 업무방식이다.

 

디버거/프로파일러 툴의 적극적인 활용

결국 최적화는 최적화를 위한 툴들을 얼마나 잘 사용하느냐에 따라 달려있다.

적극적으로 툴을 사용해서 문제를 확인하고 처리하도록 하자.

스냅드래곤, xcode 프로파일러가 그렇게 좋다더라

 

유니티 에디터에서의 프로파일링은 단순 참고용

에디터에서 측정한 성능과 타겟 플랫폼에서 측정한 성능은 전혀 다르게 나올 수 있으니 맹신하면 안 된다.

 

자동화된 테스팅 환경 구축(ex:벤치마커)

매번 테스트할 때마다 빌드, 배포에 시간이 오래 걸리니까 프로젝트 초기에 자동화된 테스트 환경 구축을 하면 좋다.

 

쓰로틀링

모바일은 PC 달리 쿨러가 없어서발열 시 자체적인 하드웨어 성능 제한을 걸어서 성능 저하 발생

최신 휴대기기일수록 취약함(성능이 좋아지지만 사이즈는 그대로라서)

ex) 처음 빌드에는 괜찮았는데 몇번 테스트 다시해보니까 성능이 갑자기 저하되면 쓰로틀링일 있음

스마트폰 쿨러로 식혀주면서 한번 측정하고 몇분뒤에 측정하는 것이 좋다.

실제로 오지현 센세는 열감지기로 핸드폰 온도 측정하고 테스트한다고

 

2. 유니티 프로파일러

커스텀 프로파일러 태그 지정

다들 잘 모르는 기능인데 위와 같이 특정 코드에 대해서 태그를 지정해서 프로파일러에서 태그에 따라 결과를 확인할 수 있다.

 

프로파일러 사용 예시 1)

프로젝트에서 프로파일러 타임라인 찍어보고 오래 걸리는 부분을 제거해주는 예시를 보여주었음.

 

프로파일러 사용 예시 2)

샘플 프로젝트인데 FPS가 6이 나오고 딱 봐도 포스트 프로세싱에 GPU 쪽 부하가 심한 것 같다고 생각은 들지만...!

 

프로파일러를 찍어보면 드로우 콜 1300개 CPU 쪽 문제임을 알 수 있다.

드로우 콜과 CPU 관계 설명

https://funfunhanblog.tistory.com/301

 

Unity) 드로우콜 DrawCall

1. 드로우콜 이란 간단하게 CPU가 GPU에게 렌더링 작업을 수행하도록 명령을 하는것이다. 게임은 실시간 렌더링 어플리케이션이다. 실시간으로 렌더링을 수행하기에, 한 프레임의 렌더링은 오브��

funfunhanblog.tistory.com

 

Profile Analyzer라고 새로 나온 기능이 있는 데 사용을 추천해주셨다.

 

 

 

GPU 프로파일링은 특정 플랫폼에서만 가능하고 쉽지 않다고 하였다.

스냅 드래곤, Xcode의 GPU 프로파일링 툴을 사용하는 것을 추천해주셨다.

 

 

2.1 다양한 병목현상

 

Fillrate 병목 현상

요즘 기기 해상도가 너무 좋아서 Fillrate로 인한 부하가 많다. 확인해보도록 하자

 

오버드로우 병목 현상

오버드로우는 말 그대로 가려진 뒷부분까지 그리는 것을 말한다. 오버드로우를 하게 되면 수많은 파티클이 중첩되면서 성능 저하를 일으킬 수 있다.

평범한 상황에서는 오버드로우로 인한 병목현상이 발생하지 않는다.

 

 

하지만 파티클에서 자주 발생하는 현상이기도 하다. Scene에서 Overdraw로 보기를 해서 오버드로우가 과하게 발생하는지 확인하도록 하자.

 

포스트 프로세싱 부하

포스트 프로세싱도 필레이트에 많은 부하를 준다.

포스트 프로세싱의 원리 : 특정 픽셀의 주변 픽셀을 가져와서 변화를 주는 샘플링 처리를 하기 때문에 필레이트와 굉장히 밀접한 연관성이 있다.

 

업스케일링 샘플링

높은 해상도를 포기하기 힘들다면 업스케일링 샘플링 기법을 추천해주셨다.

 

UI 부분의 해상도만 높은 상태를 유지하고 인게임 해상도를 낮추는 것이다.

실제로 작은 기기에서 보기 때문에 유저들은 쉽게 눈치채지 못한다.

 

오지현님의 업스케일링 샘플링에 대한 샘플 프로젝트 주소

https://github.com/ozlael/UpsamplingRenderingDemo

 

업스케일 샘플링 때문에 해상도가 낮은거지 이미지가 안좋은것이 아님!

이번에 새로 나온 기능인 URP에서는 기본적으로 업스케일링 샘플링을 공식적으로 지원한다.

위 사진은 극단적인 차이를 보여주기 위해서 인게임 해상도를 10%로 낮춘 상황이다.

 

텍스쳐 병목 현상

텍스쳐 퀄리티를 조절해서 병목 확인이 가능하다.

텍스쳐를 1/8 줄여서 플레이 해봤더니 게임의 렉이 없어졌다. -> 텍스쳐 병목이다.

 

3. 프레임 디버거

프레임 디버거를 통해서 어떻게 드로우 콜이 발생하는지 순차적으로 하나하나 볼 수 있다.

게임에서 드로우콜이 많이 발생하는 경우 확인하기 좋다.

 

또한 Batcing처리가 안되었을 때 그 이유도 나온다. ( Batcing에 대한 글은 아래 글 참고 )

https://funfunhanblog.tistory.com/302?category=818491

 

프레임 디버거를 이용한 드로우 콜 최적화 예시) 타워 디펜스

위와 같이 샘플 프로젝트를 플레이하다가 프레임 디버거로 드로우 콜을 확인해보았다.

 

드로우 콜은 245며 캐릭터 하나를 그리는데 캐릭터 이미지, 그림자, 체력바, 체력바 백그라운드 총 4개의 드로우 콜이 발생한다.

배칭이 전혀 안되어있다.

 

똑같은 성질의 이미지에 레이어를 추가해서 똑같은 애들끼리 똑같은 레이어를 적용시켜서 드로우 콜을 줄인다.

기존에는 그림자 하나를 그릴때 하나하나 그렸지만 레이어를 적용하면 그림자가 전부 한번에 그려진다.

나아가서 스프라이트 패킹, 배경 레이어를 추가하게 된다면 드로우콜을 줄일 있다.

 

 

4. 메모리 프로파일러

메모리 관리는 항상 중요하지만 모바일에서는 특히나 중요하다.

PC에서는 특정 어플리케이션에서 물리적으로 수용 가능한 메모리의 범주를 벗어나도 가상 메모리를 통해서해결할 수 있다.

그러나! 모바일에서는 메모리 시스템이 PC와는 달라서 특정 어플리케이션의 메모리 점유율이 높아지면 다른 어플리케이션의 메모리를 빼앗아서 사용하도록 설계되어있어서 메모리가 너무 커지는 순간 해당 앱을 강제종료 시켜버린다강제 종료시켜버린다. (이와 같은 이유로 모바일에서는 로딩화면에서 크러시가 많이 난다.)

실제로 내 게임도 아직 최적화를 거치지 않았는데 아이폰 개발하다가 옛날기종에서 메모리 650mb 넘어가서 어플리케이션이 강제 종료당했다.

 

메모리 프로파일러 예시 1)

메모리 프로파일러를 사용하면 모든 힙 메모리 영역을 트래킹 할 수 있다.

텍스쳐에서 어떤 텍스쳐가 많이 차지하는지 확인 가능하다. 사운드, 폰트 전부다 가능하다!

 

위의 경우는 확인해보니 dude라는 텍스쳐가 사이즈가 4096으로 설정되어있었다.

 

메모리 프로파일러 예시 2)

샘플 프로젝트인데 별거 없는 프로젝트지만 메시 하나가 20MB를 차지한다.

 

Read/Write Enabled : 스크립트를 통해서 메시에 접근할 있는지 여부(메시 변형 가능)

이것을 키게 되면 GPU, CPU 양쪽에 해당 데이터가 적재된다.

스크립트를 통해서 메시를 변경할일이 없으면 반드시 끄도록 하자

 

오디오 클립도 메모리에 올라갔을 때굉장히 큰경우가 있는데 Decompress On Load 음악 압축을 풀어서 메모리에 올리기 때문에 바꾸자.

아이폰에서는 mp3 압축돼서 메모리에 적재 가능하기때문에 mp3 권장

 

5. 텍스쳐 컴프레션

보통 게임에서 텍스쳐가 많은 부하를 차지한다.

 

텍스쳐 이미지의 복잡도, 포맷별로 적재되는 텍스쳐의 용량의 차이가 발생한다.

위의 사진을 보면 PNG, JPG는 1메가가 안되지만 인게임에서는 해당 텍스쳐가 4MB를 차지한다.

그 이유는 PNG, JPG, PSD로 저장되는 텍스쳐의 용량은 단순히 PC에서 저장되기 위한 용량을 말하는 것이지 GPU에서 사용하기 위한 포맷이 아니다.

우리가 알고 있는 텍스쳐 이미지는 GPU에서 사용하기 위해 다시 ETC, ASTC 포맷으로 전환이 일어난다.

 

또한 텍스쳐 압축 방식에 따라 미세하게 다른 차이가 발생하기도 한다.

대표적으로 사용하는 텍스쳐 압축 방식에 대하여 간단히 알아보자

 

 

PVRTC : 원본 이미지를 소수 픽셀로 뽑아내서 압축 -> 이미지가 뭉개지는 현상 발생

 

ETC : 이미지를 색상과 밝기로 나눠서 색상은 낮은 해상도, 밝기는 높은 해상도로 압축하는 방식

(인간의 눈은 색상보다 밝기에 더 민감한 원리를 이용)

 

ETC1 ETC2 있는데 ETC2 좀더 블록현상이 완화됨

 

주의할 점 : 완전 옛날 기기에서 ETC2 포맷을 사용하면 메모리에 원본 텍스쳐가 올라가버린다.

동남아 시장을 타겟으로 하면 ETC2 포맷을 사용하면 안된다.

 

PVRTC 조금 번지고 블러링 되는 느낌이 있다

ETC 의도하지 않은 색상들이 발생한다.(색상과 밝기를 나눠서 처리하기 때문에 옷깃 부분에 검은색 부분이 유난히 더 많이 보인다.)

위와 같이 텍스쳐 압축에 따라 달라지는 특징을 알아둬야 메모리를 절약하면서 퀄리티를 살릴 있다.

 

ASTC(Adaptive Scalable Texture Compression)라는 자유롭게 압축 단위를 바꾸는 압축 기법도 있다.

 

 

좌측 이미지는 캐릭터 이미지라서 민감하기 때문에 최대한 압축을 덜하는 4x4 방식을 택한다면 오른쪽 블러 이미지는 압축을 크게 해도 눈치채기 힘들기 때문에 12x12로 크게 압축을 하는 전략을 말해주셨다.

 

텍스쳐의 해상도는 2의 제곱수로 만들어야 하는 이유도 설명해주었다.

200x200짜리 이미지를 만들어도 어차피 256x256으로 변환돼서 256x256 - 200x200만큼의 공간이 낭비된다는 것이다.

 

 

 

6. Xcode Instrument

유니티 프로파일러에서는 없던 세부 내용까지 있다.

어떤 과정을 통해서 네이티브로 할당되는것까지 확인이가능하다.

 

 

7. 최적화 팁 & 트릭

여태까지 말한 것을 토대로 타겟 플랫폼에 따라 에셋의 세팅도 달라져야 한다는 것을 충분히 이해하리라 생각한다.

이런 식으로 실수 없도록 모든 에셋에 대하여 임포트 옵션을 설정할 수 있다.

 

적절한 targetFrameRate

  • 퍼즐게임이면 굳이 60프레임을 찍을 필요가 없다. : 30으로 해도 된다. -> 발열을 낮출 있음
  • 인게임에서는 60하고 UI에서는 30으로 하는 전략도 있다.
  • Adaptive Performance 통해서 CPU, GPU 부하를 탐지해서 타겟프레임 레이트를 적절하게 바꿔주는 것도 가능하다.

 

에셋 비동기 로딩 사용

AssetBundle.LoadFromFileAsync()

AssetBundle.LoadAssetAsync()

끊김 없이 에셋을 부르기 위한 방법

 

사용하지 않는 Monobehaviors 메소드 제거

  • Start, Update, FixedUpdate 제거
  • Update안의 내용이 비어있더라도 함수를 계속해서 호출하기 때문에 사용하지 않으면 지워줘야 한다.

 

Update 1000개 vs 매니저 패턴 1개

  • 성능상의 차이가 없다고 생각할 수 있지만 유니티 에디터와 C++을 왔다 갔다 하면서 함수 자체만으로 발생하는 오버헤드가 있기 때문에 매니저 패턴을 이용하는 것이 좋다.

 

비싼 API 호출 주의

  • GameObject.Find, GetComponent, Camera.main, Debug.Log
  • 위의 함수들은 잦은 호출 시 성능 저하를 일으킬 수 있는 API라서 제한적인 상황에서 사용해야 한다.
  • 따라서 자주 쓰이는 변수의 경우 Awake나 Start에서 캐싱해놓고 사용하도록 하자

 

Allocating API 주의

  • GetComonents, Animator.parameters와 같이 List 형태의 데이터를 받아오는 API를 호출하게 되면 해당 데이터를 담아두는 변수를 그 즉시 생성하기 때문에 사용할 때마다 계속해서 리스트를 생성하는 것이기 때문에 유의해서 사용해야 한다. 

 

정적 데이터 파싱 주의

  • 인게임에서 필요할때마다 xml, json 불러오는것보다 so 바꿔서 처리하는게 좋다.
반응형