Data Loading...
유니티 5 게임 프로그래밍 최적화 Flipbook PDF
유니티 5 게임 프로그래밍 최적화
172 Views
66 Downloads
FLIP PDF 1.3MB
143
최대 성능을 위한
유니티 5 게임 프로그래밍 최적화 크리스 디킨슨 지음
유건곤, 최종우 옮김
143
최대 성능을 위한
유니티 5 게임 프로그래밍 최적화 크리스 디킨슨 지음
유건곤, 최종우 옮김
이 도서는
Unity 5 Game Optimization( PACKT publishing) 의 번역서입니다
표지 사진 이현종 이 책의 표지는 이현종님이 보내 주신 풍경사진을 담았습니다. 리얼타임은 독자의 시선을 담은 풍경사진을 책 표지로 보여주고자 합니다. 사진 보내기 [email protected]
최대 성능을 위한 유니티 5 게임 프로그래밍 최적화 초판발행 2017년 6월 30일 지은이 크리스 디킨스 / 옮긴이 유건곤, 최종우 / 펴낸이 김태헌 펴낸곳 한빛미디어(주) / 주소 서울시 마포구 양화로 7길 83 한빛미디어(주) IT출판부 전화 02-325-5544 / 팩스 02-336-7124 등록 1999년 6월 24일 제10-1779호 / ISBN 978-89-6848-850-4 95000 총괄 전태호 / 기획 김상민 / 편집 조수현 디자인 표지·내지 여동일, 조판 김경수 영업 김형진, 김진불, 조유미 / 마케팅 송경석, 조승모 변지영 이 책에 대한 의견이나 오탈자 및 잘못된 내용에 대한 수정 정보는 한빛미디어(주)의 홈페이지나 아래 이메일로 알려주십시오. 한빛미디어 홈페이지 www.hanbit.co.kr / 이메일 [email protected] Published by HANBIT Media, Inc. Printed in Korea Copyright ⓒ Packt Publishing. First published in the English language under the title ‘Unity 5 Game Optimization’(9781785884580). This translation is published and sold by permission of Packt Publishing, which owns or controls all rights to publish and sell the same. 이 책의 저작권은 Packt Publishing와 한빛미디어(주)에 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 복제 및 무단 전재를 금합니다. 지금 하지 않으면 할 수 없는 일이 있습니다. 책으로 펴내고 싶은 아이디어나 원고를 메일([email protected])로 보내주세요. 한빛미디어(주)는 여러분의 소중한 경험과 지식을 기다리고 있습니다.
지은이 · 옮긴이 소개
지은이_ 크리스 디킨슨Chris Dickinson
크리스 디킨슨은 어릴 적부터 과학, 수학, 비디오 게임에 흥미가 있었다. 2005년 리 즈대학교Universty of Leeds에서 전자 물리학 박사 학위를 획득했고, 졸업 즉시 과학 연구를 위해 실리콘 밸리로 떠났다. 하지만 연구가 적성에 맞지 않아 소프트웨어 분야로 이직 을 했다. 지난 10여 년간 소프트웨어 개발자로 경력을 쌓아 시니어 개발자가 됐다. 그 는 주로 소프트웨어 자동화와 내부 테스트 툴을 개발했지만 비디오 게임에 대한 열정 은 결코 식지 않았고, 2010년 『게임과 시뮬레이션 프로그래밍』 논문으로 학사 학위를 받으며 3D 그래픽과 게임 개발에 대한 지식을 쌓았다. 저서로는 게임 물리의 기초를 집필한 『Learning Game Physics with Bullet Physics and OpenGL』 (Packt, 2013)이 있으며, 소프트웨어 개발을 하는 틈틈이 독 립 게임 프로젝트도 진행하고 있다. 옮긴이_ 유건곤
구 가천의대학교 (현 가천대학교) 정보공학부를 졸업하였다. 2010년부터 금융권 개발자 로 베스트투자증권, 한화증권, LIG투자증권, 우리투자증권, NH투자증권 같은 증권사 의 SI 개발에 참여했다. 그러다 시스템 트레이딩 회사로 이직하여 해외 트레이딩 시스템 분석 개발 업무를 하였다. SBS게임아카데미에서 2년간 강의를 하였고 현재는 한국스마 트ICT융합협회 소속으로 웹타임, 멀티캠퍼스 등의 교육센터에서 강의를 하고 있다. 옮긴이_ 최종우
대원외국어고등학교 중국어과와 고려대학교 경영학과를 졸업했으며 프로그래밍에 흥 미를 느껴 개발자로 진로를 전환했다. 현재는 SBS게임아카데미에서 프로그래밍 강사 를 거쳐 서울대 CAP-Lap 연구실에서 석사 과정 중에 있다.
지은이 서문
지난 5년간 게임 분야에서 엄청난 양의 지식을 얻을 수 있었다. 이는 동료, 지도 교수, 친구, 가족의 응원이 없이는 불가능한 일이었다. 게임 개발 학교에 다니느라 일상생활 이 굉장히 불규칙한 것을 이해해준 동료에게 감사의 인사를 전한다. 최신 게임 개발 정 보와 핵심 자료를 알려준 지도 교수께도 감사를 표하고 싶다. 늘 넘치는 응원과 호기심 으로 지켜봐 준 친구들도 감사하다. 또한 지금까지 살아오고 사랑하며 배울 기회를 준 가족에게도 감사하다. 끝으로 늘 좋은 아내이자 최고의 친구가 되어준 제이미Jamie에 게, 창의적인 감각을 유지할 수 있도록 밤늦게까지 늘 돌봐주고 도와줘 고맙다는 말을 전하고 싶다.
옮긴이 서문
빠르게 변화하는 시대에 예전처럼 직접 엔진을 만들어 사용하기에는 시간과 비용이 너무 많이 필요하여서 많은 회사 및 개인들이 유니티 엔진을 사용하고 있다. 게임을 빠르고 편리하게 만들 수 있도록 게임 엔진이 많은 것을 도와주고 있지만 한 가 지 우려되는 점이 있다. 쉽고 편리하게 만들 수 있게 되면서 엔진 안쪽의 내용을 파악 하기가 힘들어졌다는 점이다. 초기 개발에는 이러한 의문이 전혀 들지 않는다. 자신의 게임을 빠르고 쉽게 만들어서 다른 사람들에게 즐거움을 전하며 이익을 거둘 생각에 게임을 만들지만, 막상 게임을 출시하고 나면 미처 고민하지 못했던 문제점들이 눈에 띈다. 각기 다른 핸드폰에서 동작하다 보니 게임은 느려지기 일쑤다. 물리적, 그래픽적 문제 들이 생겨나지만 무엇을 잘못한 건지 알 수조차 없다. 그러한 여러 문제를 해결하려면 우선 유니티 엔진의 내면을 깊이 이해하고, 엔진과 리소스 엔진에 부담이 안 되도록 하 는 ‘최적화’를 해야 한다. 이 책은 그러한 문제점을 해결하기 위한 여러 가지 프로그래밍 최적화 기술을 담고 있 다. 많은 사람이 앞으로 게임을 만드는 데 유니티 엔진을 사용하고, 게임 출시 이후 맞닥 뜨릴 여러 문제를 개선하고 최적화하는 데 이 책이 좋은 지침이 되길 기원한다. 마지막으로 같이 책을 번역한 최종우 강사에게 고마움을 전하고 더운데도 열심히 수 업에 나와 공부하는 학생들과 나를 믿고 같이 함께 하는 학원 강사와 직원들 그리고 가 족들에게 고마움을 전한다. 유건곤
이 책은 유니티 5의 최적화를 다루지만, 여기서 배울 수 있는 기본 개념들과 기법들은 유니티를 넘어서 일반 게임 개발에도 응용할 만한 내용이 많다. 원전을 최대한 살리려고 노력하며 가능하면 독음 번역을 최소화하여 해석하되, 영어 를 함께 적는 방식으로 구글 검색에 도움이 되도록 번역했다. 게임 개발은 저자의 말처럼 즐거운 일이다. 모쪼록 이 책이 어려움을 겪는 개발자들에 게 게임 개발의 즐거움을 되찾아 주고, 개발자는 게이머에게 이 책으로 되찾은 즐거움 을 다시 퍼뜨려 주길 바란다. 역자 서문을 쓰는 이 순간까지 번역을 진행하는 내내 밤낮으로 나를 괴롭히던 기록적인 폭염이 잦아들고 있다. 에어컨을 찾아서 카페를 전전해야 했던 지난 몇 주와는 바람이 다른 것을 느낀다. 이 책을 읽는 독자에게도 이 선선한 바람이 전해지기를… 끝으로 이 좋은 책을 번역할 기회를 주신 한빛미디어, 나에게 C 프로그래밍을 가르쳐 준 유건곤 개발자, 새로운 도전을 음으로 양으로 응원해주신 심충식 아저씨, 그리고 키 네시스를 비롯한 인터넷의 얼굴 모를 프로그래밍 고수들, 가족 그리고 하나님께 감사 하다. 최종우
들어가며
UXUser eXperience01는 모든 게임의 핵심 요소다. 게임의 스토리와 방식은 UX의 일부에 지나지 않는다. 그래픽이 얼마나 자연스럽게 움직이는가, 멀티플레이어 서버와 연결 이 얼마나 자연스러운가, 플레이어의 입력에 얼마나 민첩하게 반응하는가, 심지어 최 종 배포본의 파일 크기로 인한 다운로드 시간마저도 UX의 범주에 들어간다. 저렴하 면서도 막강한 기능의 게임 개발 엔진 ‘유니티’가 게임 개발의 진입 장벽이 낮추고 있 지만, 게임의 콘텐츠와 품질에 대한 사용자의 기대는 나날이 높아지고 있다. 이제 게임 개발자는 게임의 모든 것이 사용자와 평론가에게 낱낱이 분석된다는 사실을 받아들여 야 한다. 성능 최적화의 목표는 UX와 관련이 있다. 최적화가 잘 안 된 게임은 초당 프레임 수fps, 가 낮아 화면이 끊기거나 입력 랙Lag으로 인한 지연 현상 때문에 게임을 즐
frame per second
기기 어려운 정도로 실행이 원활하지 않을 수 있다. 최악의 경우에는 에러로 게임이 멈 추기도 한다. 긴 로딩 시간, 그래픽 깨짐, 지나치게 많은 배터리 소모 (특히 모바일 게임에서 중요한 요소다) 모두 최적화와 관련이 깊다.
이러한 문제가 하나라도 발생하는 상황은 개발자에게 그야말로 악몽이 아닐 수 없다. 사용자는 개발자가 잘 만든 99가지를 놔두고 문제가 발생한 단 하나에 집중해 리뷰하 기 때문이다. 성능 최적화의 핵심은 ‘컴퓨팅 자원의 적절한 분배’다. CPU와 GPU의 클럭, 메인 시스템 메모리RAM의 용량, 비디오 메모리GPU RAM의 자원량과 대역폭과 같 은 모든 자원이 적재적소에 분배되고 병목현상 없이 우선순위에 따라 작동하도록 하 는 게 바로 ‘성능 최적화’다. 아주 짧은 게임의 멈칫거림이나 늘어지는 현상은 사용자 의 몰입을 방해하여 개발자가 의도한 경험을 제대로 전하지 못하게 만들기 때문에 주 의해야 한다. 01 옮긴이주_ 사용자 경험, 즉 어떤 사물이나 서비스를 경험하는 데 따른 개인의 감정이나 태도를 뜻한다.
게임을 개발하다 보면 때로는 적절한 타협이 필요하다. 제한된 자원과 시간으로 가장 빠르게, 효과적으로 최적화할 길은 언제나 분명히 있다. 이 말인즉슨, 타협할 수 있는 ‘선’이 반드시 있다는 것이다. 적절한 선을 정하지 않으면 지극히 작고 알아채기조차 어려운 사소한 문제에 시간을 허비하게 될 수도 있다. 이 선을 결정하는 데 가장 좋은 질문은 “사용자가 이 문제를 인식할 수 있는가”다. 만약 이 질문에 대한 대답이 “아니 요”라면 성능 개선은 필요 없다. 소프트웨어 개발의 오래된 격언 중 하나는 “어설픈 최 적화는 만 가지 악의 근원”이라는 말이다.02 이 말은 어설픈 최적화가 불필요한 재작업 을 낳는 중대한 잘못이란 뜻이다. 문제가 될지 안 될지 알지 못한 채 문제라는 의심만 으로 코드를 수정하는 것은 한정된 개발 자원을 쓸데없이 낭비하는 것에 불과하다. 이 책은 개발자들에게 최적화를 실행하기 위한 툴과 지식, 기술뿐 아니라 어디에서 기 인한 문제인지 원인을 발견하는 것까지를 다룬다. 문제는 CPU, GPU, RAM과 같은 하드웨어뿐 아니라 물리 엔진을 비롯한 프로그램의 하부 시스템, 유니티 자체의 결함 에 의해서도 발생할 수 있다는 데 있다. 최적화는 같은 하드웨어에서 더 많은 표현과 처리를 가능하게 할 뿐 아니라 더 흥미롭고 다채로운 게임을 만들게 해준다. 최적화 차 이가 게임이 시장에서 살아남는 밑거름이 될 수 있음을 기억해야 한다.
이 책에서 다루는 내용 1장 성능 문제를 발견하기에서는 유니티 프로파일러Unity Profiler를 사용해 앱을 분석하고 병목현상을 찾는 문제 분석 방법을 소개한다.
02 옮긴이주_ 많은 개발자가 실제로 눈에 보이지 않는 최적화를 위해 97%의 시간을 할애한다는 도널드 커누스 의 말을 인용했다.
2장 스크립팅 전략에서는 유니티 C#의 대표적인 예제들과 오버헤드03 최소화, 객체 간 의 상호작용 개선 등을 다룬다. 3장 배칭의 유용성에서는 유니티의 정적 배칭04과 동적 시스템을 설명하고 렌더링 시스 템의 부하를 줄이는 법을 설명한다. 4장 아트 자원을 활용하라에서는 아트 자원들 속에 있는 기술과 함께 불러오기, 압축, 인코딩 과정에서 빠지기 쉬운 함정을 피하는 법을 배운다. 5장 더 빠른 물리에서는 2D와 3D 유니티 엔진의 물리 시스템을 상세히 소개하고 물리 객체들을 적절히 구성해 성능을 높이는 비법을 소개한다. 6장 역동적인 그래픽에서는 렌더링 시스템의 깊은 곳까지 탐험한다. CPU, GPU에 의 한 렌더링 병목현상을 개선하는 방법과 모바일 기기를 위한 특별한 기술을 배운다. 7장 메모리 관리의 주인에서는 ‘모노 프레임워크Mono Framework’라는 유니티 엔진의 내부 를 살펴본다. 메모리가 어떻게 관리되는지, 프로그램을 힙 할당과 ‘실시간 폐공간 회수 05
’로부터 보호하는 법을 소개한다.
8장 전략적 기술과 팁에서는 작업 속도와 화면 관리에 도움이 되는 팁을 알려준다.
03 옮긴이주_ 특정한 목표를 달성하기 위해 간접 혹은 추가로 요구되는 시간, 메모리, 대역폭 혹은 다른 컴퓨터의 자원을 말한다. (출처 : 두산백과, http://terms.naver.com/entry.nhn?docId=2829829&cid=40942&c
ategoryId=32828) 04 옮긴이주_ 배칭은 3D에서 여러 원형과 객체를 각각 연산하지 않고 하나의 덩어리로 만들어 연산하는 방법이다. 05 옮긴이주_ 가비지 콜랙션Garbage Collection을 말한다. 동적 데이터 구조에서 힙을 효과적으로 사용하기 위해 사용 되지 않는 힙 공간의 할당을 해제하는 것이다.
이 책의 대상 독자 이 책은 중급 및 고급 유니티 개발자를 위한 책이다. 게임 성능을 개선하거나 병목현상 을 해결하는 데 알아야 할 최적화 기법이 이 책에 담겨있다. 그 병목현상이 CPU 과부 하에 의한 것인지, 순간적인 과부하 때문인지, 느린 메모리 접근 때문인지, 조각 현상 때문인지, 폐공간 회수 때문인지, 적은 GPU 처리 용량06 때문인지, 아니면 메모리 대 역폭이 문제인지 이 책을 통해 원인을 분석하고 문제를 해결하는 법을 알려준다. 메모 리 관리와 스크립팅 부분에서는 C#에 대한, 셰이더 최적화 파트에서는 기초적인 컴퓨 터 그래픽 (아마도 3D)에 대한 기본적인 이해를 전제로 글을 썼다.
예제 코드 내려받기 보조 자료 (예제 코드, 연습문제 등)는 http://www.hanbit.co.kr/scr/2850에서 내려받 을 수 있다.
06 옮긴이주_ GPU 필레이트Fillrate는 그래픽 칩이 처리할 수 있는 시간당 최대 픽셀 수를 뜻한다.
차례
chapter
1 성능 문제 발견하기
19
1.1 유니티 프로파일러
20 21
1.1.1 프로파일러 시작하기 1.1.2 프로파일러 창
25
1.2 성능 분석을 잘 하는 법
32
1.2.1 스크립트 존재 여부 확인
33
1.2.2 스크립트 실행 횟수 확인
34 35
1.2.3 프로파일링 중 코드 변환하지 않기 1.2.4 내부 방해 요인 줄이기
35
1.2.5 외부 방해 요소 줄이기
37
1.3 코드 삽입을 통한 목표 프로파일링
37 38
1.3.1 프로파일러 스크립트 넣기 40
1.3.2 자체 CPU 프로파일링
1.4 프로파일링한 자료를 저장하고 불러오는 방법 49
1.4.2 프로파일러 데이터 불러오기
1.5 프로파일링과 분석에 대한 마지막 당부 54
1.5.2 잡음(방해) 줄이기
55
1.5.3 문제에 집중하기
chapter
2 스크립트 전략
53
53
1.5.1 프로파일러 이해하기
1.6 요약
44 46
1.4.1 프로파일러의 데이터 저장하기
56
57
2.1 컴포넌트 참조 캐시
57
2.2 컴포넌트를 가장 빠르게 불러오는 방법
59
2.3 빈 호출 함수 줄이기
62 64
2.4 실행 중에 Find( )와 SendMessage( ) 함수 피하기 67
2.4.1 전역 클래스
68
2.4.2 싱글턴 컴포넌트
73
2.4.3 기존 객체로부터 참조하기 76
2.4.4 전역 메시지 시스템
2.5 사용하지 않는 스크립트와 객체 비활성 하기
88
2.5.1 시야에 따라 객체를 비활성 하기(프러스텀 컬링) 90
2.5.2 객체를 거리에 따라 비활성 하기
2.6 거리 대신 거리 제곱 사용하기
91
2.7 문자열 속성을 검색하지 않기
93
2.8 업데이트, 코루틴, 반복 호출
96
2.9 트랜스폼을 바꿀 때 캐싱 고려하기 2.10 더 빠른 게임 오브젝트 빈 참조 확인 2.11 요약
chapter
104
3 배칭의 유용성
107
3.1 드로우 콜
108 110
3.2 재질과 셰이더 3.3 동적 배칭
113 115
3.3.1 정점 속성 3.3.2 균등 스케일링
116
3.3.3 동적 배치 요약
117
3.4 정적 배칭 3.4.1 정적 선언
118 118
102 104
89
119
3.4.2 메모리 요구사항 120
3.4.3 재질 참조
120
3.4.4 정적 배치의 주의점 122
3.4.5 정적 배치 요약
3.5 요약
chapter
122
4 당신의 아트 자원을 활용하라 4.1 오디오
125
125 126
4.1.1 오디오 파일을 불러오기
129
4.1.2 음원 코딩 포맷과 음원 품질 131
4.1.3 오디오 성능 개선
4.2 텍스처 파일
137
4.2.1 압축 형식
137 139
4.2.2 텍스처 성능 개선
4.3 메시와 애니메이션 파일들
150 150
4.3.1 폴리곤 개수 줄이기
4.3.2 필요한 것만 불러들이고 계산하기
152
4.3.3 미리 계산된 애니메이션 고려하기
153
4.3.4 유니티가 메시를 최적화하도록 하기 4.3.5 메시 합치기
4.4 요약
155
154
154
chapter
5 더 빠른 물리
157
5.1 물리 엔진의 내부
158 158
5.1.1 물리와 시간
163
5.1.2 정적과 동적 충돌체 5.1.3 충돌 체크
163 164
5.1.4 충돌체의 종류 5.1.5 충돌 행렬
166 166
5.1.6 충돌 개체의 활성화 및 비활성화 상태 167
5.1.7 광 투사와 물체 투사
5.2 물리 성능 최적화
167 167
5.2.1 씬 설정
170
5.2.2 정적 충돌자를 정확하게 사용하기 5.2.3 충돌 행렬을 최적화하기
171
5.2.4 불연속 충돌 우선 사용하기
172
5.2.5 고정된 업데이트 빈도 수정
173
5.2.6 최대 허용 시간 단계 바꾸기
174 175
5.2.7 던지기와 경계-부피 확인의 최소화 5.2.8 복잡한 입체 충돌체 피하기
177 180
5.2.9 복잡한 물리 구성 요소를 피하기 180
5.2.10 물리 객체를 비활성화하자 5.2.11 해결 반복 횟수 바꾸기
182
5.2.12 충돌 인형 최적화하기
183
5.2.13 언제 물리를 써야 할지 판단
185
5.2.14 유니티 5로 업그레이드하기
186
5.3 요약
187
chapter
6 역동적인 그래픽
189
6.1 렌더링 문제 프로파일링 하기
190 192
6.1.1 GPU 프로파일링
195
6.1.2 프레임 디버거
196
6.1.3 무작위 삭제 테스트 196
6.1.4 CPU에 묶임
6.2 프론트엔드 병목현상
199 200
6.2.1 세부 표현의 정도
202
6.2.2 GPU 표면 처리 끄기
6.2.3 테셀레이션(쪽 맞추기, 채우기) 줄이기
6.3 백엔드 병목현상 6.3.1 필레이트
203 204 218
6.3.2 메모리 대역폭
222
6.3.3 비디오 메모리의 한계
6.4 광원과 그림자
203
224
6.4.1 포워드 렌더링
225
6.4.2 디퍼드 셰이딩
226 227
6.4.3 정점 광원 셰이딩(구버전) 227
6.4.4 실시간 그림자 표현
6.5 모바일을 위한 최적화
230
6.5.1 드로우 콜 최소화 6.5.2 머티리얼 수 줄이기
230 231 231
6.5.3 수량과 텍스처 크기 줄이기
6.5.4 텍스처 크기를 2의 지수인 정사각형으로 만들기 6.5.5 셰이더에는 가능하면 낮은 해상도의 자료형을 사용하자 6.5.6 알파 테스팅 하지 않기
6.6 요약
232
232
231 232
chapter
7 메모리 관리의 주인 7.1 모노 플랫폼
233 234 236
7.1.1 컴파일 과정
7.2 메모리 사용 최적화
239 239
7.2.1 유니티의 메모리 영역
249
7.2.2 값 형식과 참조 형식
264
7.2.3 데이터 배치의 중요성 266
7.2.4 유니티 API
266
7.2.5 foreach 반복문 7.2.6 코루틴
268
7.2.7 클로저
268 269
7.2.8 닷넷 라이브러리 함수 270
7.2.9 임시 작업 버퍼
270
7.2.10 객체 풀링
7.3 Prefab 풀링
273 277
7.3.1 풀링 가능한 구성 요소
280
7.3.2 Prefab 풀링 시스템 284
7.3.3 Prefab Pools 7.3.4 객체 활성화
286 287
7.3.5 인스턴스 미리 활성화 7.3.6 객체 비활성화 7.3.7 Prefab 풀링 테스트
288 289 291
7.3.8 Prefab 풀링과 씬 로딩 7.3.9 Prefab 풀링 요약
7.4 유니티와 모노의 미래 7.5 요약
296
291
293
chapter
8 전략적 기술과 팁
297
8.1 에디터 단축키 팁
297 298
8.1.1 게임 오브젝트 8.1.2 씬 창
298
8.1.3 배열
299 299
8.1.4 인터페이스 300
8.1.5 그 외
8.2 에디터 인터페이스 팁들
300
300
8.2.1 일반
303
8.2.2 조사 창 8.2.3 프로젝트 창
305
8.2.4 계층 구조 창
306
8.2.5 씬과 게임 창
307
8.2.6 플레이 모드
308
8.3 스크립팅 팁
309
8.3.1 일반
309
8.3.2 특성
310 313
8.3.3 로그 기록
313
8.3.4 유용한 링크들
8.4 에디터/메뉴 개인화 팁 316
8.5 외부 팁 8.5.1 다른 팁들
8.6 요약
314
319
318
chapter
1
성능 문제 발견하기
소프트웨어 성능 평가의 대부분은 매우 과학적인 방법으로 이루어진다. 가장 먼저 성능 지표01를 측정한다. 실제로 일어날 만한 시나리오에 따라 성능 부하를 테스 트한다. 실험 케이스들의 측정 자료를 수집하고 분석해 병목현상을 발견한다. 발 견된 병목현상의 근본적인 문제를 찾는다. 설정을 바꾸거나 코드를 수정하며 근본 원인을 고친다. 이를 반복한다. 게임 제작은 예술이다. 하지만 예술성을 지녔다고 해서 객관적이고 과학적인 방법 을 사용하지 말아야 할 이유는 없다. 때로는 하드웨어 문제로 게임을 즐기지 못할 수도 있다. 그러므로 다양한 하드웨어 환경에서 프로그램을 테스트하고 이상적인 실행 환경과 비교해야 한다. 그래야 다양한 각도로 문제를 바라보고 해결 방법을 찾을 수 있다. 이 장에서는 이러한 과정을 수행하기 위한 다양한 툴과 방법을 소개한다. 이러한 툴과 방법을 통해 개발 중인 유니티 프로그램의 근본적인 문제를 발견하고 어디를 고쳐야 하는지 알게 된다. 문제를 해결하기 위한 사전 준비 단계라 할 수 있다. 이 어지는 장들에서는 실질적인 문제 해결 방법을 다룬다. 가장 먼저 유니티 프로파일러Unity Profiler의 핵심 기능을 살펴보자. 그리고 여러 가 지 스크립팅 기술을 통해 잘 보이지 않는 병목현상을 추적해 나가는 방법을 알아 보고 여러 가지 팁도 살펴본다.
01 옮긴이주_ 최대 메모리 사용량, CPU 사용량, 동시 사용자 수가 여기에 해당한다.
1장 성능 문제 발견하기
_ 19
1.1
유니티 프로파일러
유니티 프로파일러는 유니티 에디터Unity Editor에 내장된 분석 툴로, 병목현상의 주 원인을 추적하는 데 필요한 유니티의 여러 가지 요소들의 실시간 통계 자료를 보 여준다. 유니티 엔진의 요소별 CPU 사용량
●
●
●
●
●
●
렌더링 통계 프로그래밍 할 수 있는 파이프라인 단계 메모리 사용량 통계 오디오 사용량 통계 물리 엔진 사용량 통계
NOTE
유니티 5의 모든 버전에는 프로파일러가 포함되어 있다. 심지어 퍼스널 에디션Personal Edition에서도 프 로파일러를 기본 제공한다. 단, 유니티 4 퍼스널 에디션은 유니티 4 프로 에디션을 구매하거나 유니 티 5로 업그레이드해야 유니티 프로파일러를 이용할 수 있다.
이러한 다양한 통계 자료 수집에는 추가적인 자원이 소모된다. 계측 플래그들은 컴파일러를 통해 활성화되며 실시간 로그 수집을 위해 프로그램에 자동으로 코드 를 추가한다. 그로 인해 간접적이기는 하지만 CPU와 메모리 자원이 추가로 소모 된다. 프로파일러를 켜고 끌 때 소프트웨어가 멈칫거릴 수 있다. 프로파일러를 켜 는 데 드는 추가 자원이 적지 않기 때문이다. 그러므로 벤치마크나 프로파일링을 할 때는 에디터 모드를 사용하지 않는 게 좋다. 에디터 모드의 여러 객체와 인터페 이스도 추가적인 메모리 자원을 소모하기 때문이다. 그러므로 데이터 수집이 필요 한 소프트웨어 테스트는 독립적으로 실행해야 한다.
TIP
유니티 프로파일러를 개발 중인 프로그램과 연결할 줄 안다면 ‘1.1.2 프로파일러 창’은 건너뛰어도 좋다.
20 _
1.1.1 프로파일러 시작하기 이제부터 소개할 다양한 방법을 통해 유니티 프로파일러와 개발 중인 프로그램을 연결할 수 있다.
유니티 에디터 플레이 모드Unity Editor Play mode나 독립 실행 애플리케이션을 컴퓨터에서 직
●
접 프로파일링 하기 유니티 에디터 자체를 프로파일링 하기
●
●
●
●
●
유니티 웹플레이어Unity Webplayer에서 로컬 인스턴스Instances로 애플리케이션을 실행하기 iOS 기기 (아이패드 또는 아이폰)에서 원격으로 실행하기 안드로이드 기기 (구글 넥서스, 삼성 갤럭시 등)에서 원격으로 실행하기 프로파일러를 설정하기 위한 각각의 요구사항들
유니티 에디터 플레이 모드 또는 독립 실행 애플리케이션 프로파일링
프로파일러를 실행하려면 에디터를 켜야 한다. 프로파일러는 에디터뿐 아니라 해당 컴퓨터에서 직접 실행했거나 외부 기기에서 실행 중인 애플리케이션에 대 한 원격 프로파일링을 지원한다. 프로파일러는 유니티 에디터의 리본 메뉴 중 Window → Profiler (Ctrl + 7)를 클릭해 활성화할 수 있다. 만약 에디터 플레이 모드로 애플리케이션을 이미 실행했다면 프로파일러 창Window에서 여러 가지 데이 터를 바로 확인할 수 있다. 그림 1-1 프로파일러 메뉴
1장 성능 문제 발견하기
_ 21
TIP
에디터의 플레이 모드로 실행하지 않은 외부 프로젝트를 프로파일러와 연결하려면 프로젝트를 빌드 할 때 개발 모드Development Mode와 프로파일러 자동 연결Autoconnect Profiler 기능을 활성화해야 한다.
프로젝트를 에디터 내부의 플레이 모드를 통해 프로파일링 할 것인지, 아니면 독 립적으로 빌드된 프로젝트로 프로파일링 할 것인지는 프로파일러 창의 액티브 프 로파일러Active Profiler 리본 메뉴에서 정할 수 있다. 그림 1-2 액티브 프로파일러
에디터 프로파일링
개인이 제작한 스크립트같이 편집기 자체를 프로파일링 해야 할 경우 [그림 1-3] 처럼 액티브 프로파일러Active Profiler 옵션에서 Editor를 선택한 후 Profile Editor (좌측) 옵션을 체크한다. 그림 1-3 에디터 프로파일링
유니티 웹플레이어 프로파일링
프로파일러는 유니티 웹플레이어Unity Webplayer로 실행 중인 웹 애플리케이션을 브 라우저에서 확인할 수 있다. 웹 애플리케이션의 데이터 수집뿐 아니라 다양한 웹 브라우저에서 실행 중인 애플리케이션의 이상 유무까지도 확인할 수 있다.
22 _
웹플레이어 애플리케이션을 빌드할 때 개발 모드Development Mode를 활성화하면 프 로파일링을 이용할 수 있다. 1. 컴파일한 (혹은 빌드한) 웹플레이어 애플리케이션을 웹브라우저에서 실행한 후 Alt 키 (맥의 옵션 키)를
누른 상태로 웹플레이어 내부를 마우스 오른쪽 버튼으로 클릭해 풀다운 메뉴를 연다.
다음 [그림 1-4]같이 Release Channel → Development를 선택한다.
NOTE
Release Channel 옵션을 변경하면 웹플레이어 애플리케이션이 재시작된다 (게임을 하는 중이면 저장해 야 한다).
그림 1-4 개발 채널
2. 다음 [그림 1-5]처럼 유니티 에디터를 켜고 프로파일러 창을 활성화한다. 프로파일러 창 의 액티브 프로파일러Active Profiler 메뉴를 연다. 윈도Windows OS 컴퓨터라면 WindowsWeb
Player (COMPUTER NAME)를, 맥 OS 컴퓨터라면 OSXWeb Player (COMPUTER NAME) 를 선택한다 (에디터 자체를 프로파일할 때는 Editor를 선택한 곳에서 Webplayer를 선택한다). 그림 1-5 윈도 웹플레이어
1장 성능 문제 발견하기
_ 23
iOS 기기 원격 프로파일링
프로파일러는 와이파이WiFi를 통해 아이패드나 아이폰과 같은 iOS 기기를 원격으 로 접속할 수 있다. 다음은 iOS 기기와 프로파일러를 연결하는 과정이다.
NOTE
맥 OS 컴퓨터에서만 프로파일러가 iOS 기기로 원격 접속할 수 있다.
1. 프로젝트를 빌드할 때 개발 모드와 프로파일러의 자동 연결 기능을 설정한다. 2. 맥 OS와 iOS 기기는 같은 와이파이 무선 네트워크 혹은 애드혹Ad-hoc 와이파이에 접속한다. 기기를 USB나 라이트닝 케이블Lightning Cable로 맥과 연결한다. 3. iOS
4. 애플리케이션을 Build & Run 메뉴로 빌드한다. 5. 유니티 에디터를 통해 프로파일러 창을 열고 액티브 프로파일러Active Profiler 메뉴에서 iOS 기 기가 인식됐는지 확인하고 선택한다.
이제 프로파일러 창을 통해 iOS 기기 정보를 읽을 수 있다.
TIP
프로파일러는 데이터 송수신에 54998번과 55511번 포트를 사용한다. 방화벽을 사용할 경우 반드 시 두 포트의 외부 접속을 허용해야 프로파일링을 할 수 있다.
안드로이드 기기 원격 프로파일링
유니티 프로파일러는 와이파이 연결 또는 ADBAndroid Debug Bridge 툴 이 두 가지 방 법을 통해 안드로이드 기기 (안드로이드 스마트폰과 태블릿)와 연결할 수 있다. ADB는 안 드로이드 SDKSoftware Development Kit에 포함된 디버깅 툴이다. 와이파이로 프로파일링 하는 방법은 다음과 같다. 1. 프로젝트를 빌드할 때 개발 모드와 프로파일러의 자동 연결 기능을 설정한다. 2. 컴퓨터와 안드로이드 기기는 같은 와이파이 무선 네트워크에 접속한다.
24 _
케이블로 안드로이드 기기와 컴퓨터를 연결한다. 3. USB
4. 애플리케이션을 Build & Run으로 빌드한다. 5. 유니티 에디터를 통해 프로파일러 창을 열고 액티브 프로파일러Active Profiler 메뉴에서 안드로 이드 기기가 인식됐는지 확인하고 선택한다.
ADB를 이용한 프로파일링 방법은 다음과 같다. 1. 윈도 프롬프트Prompt 창 (CMDCommand 창)에서 adb devices 명령어를 실행해 ADB가 안드로이 드 기기를 인식했는지 확인한다 (만약 기기가 인식되지 않았다면 적절한 드라이버가 설치됐는지, 안드로이드 기기에 서 USB 디버깅을 허용했는지 확인한다).
NOTE
CMD 창에서 ADB 명령어를 입력했는데 adb devices 명령어가 없다는 에러 메시지가 나오면 환 경설정에 안드로이드 SDK 폴더를 추가해야 한다.
2. 프로젝트를 빌드할 때 개발 모드와 프로파일러의 자동 연결 기능을 설정한다. 케이블을 이용해 안드로이드 기기와 컴퓨터를 유선으로 연결한다. 3. USB
4. 애플리케이션을 Build & Run 메뉴로 빌드한다. 5. 유니티 에디터를 통해 프로파일러 창을 열고 액티브 프로파일러Active Profiler 메뉴에서 안드로 이드 기기가 인식됐는지 확인하고 선택한다.
이제 프로파일러 창을 통해 안드로이드 기기의 정보를 읽을 수 있다.
1.1.2 프로파일러 창 지금부터는 프로파일러의 인터페이스와 핵심 기능을 알아보자.
NOTE
이 책은 유니티 5.x 버전의 프로파일러를 기준으로 작성됐다. 따라서 유니티 4 프로패셔널에서 제공 하는 프로파일러는 이 책에 소개된 인터페이스와 다르거나 일부 기능이 없을 수도 있다.
1장 성능 문제 발견하기
_ 25
프로파일러 창은 크게 세 부분으로 나누어진다. 제어 영역Controls
●
●
●
타임라인 영역Timeline 작업 분석 영역Breakdown View
각 영역의 구성은 [그림 1-6]과 같다. 그림 1-6 프로파일러 창
제어 영역
프로파일러에서 가장 상단에 위치한 툴바는 프로파일러로 수집할 정보량을 조절 하는 데 사용한다.
프로파일러 추가Add Profiler 프로파일러는 기본적으로 CPU와 렌더링의 상태를 비롯한 유
●
니티 엔진의 여러 요소를 보여준다. 프로파일러 추가 옵션을 통해 비활성화된 요소를 추 가할 수 있다.02
02 옮긴이주_ 활성화돼 있는데 보이지 않는 창들은 스크롤을 옆으로 움직이거나 창을 늘리면 볼 수 있다.
26 _
기록Record 프로파일러가 어떤 데이터를 기록할지 결정한다. 에디터의 플레이 모드가 실행
●
중이거나 프로파일러 에디터 옵션이 켜진 상태에서만 데이터가 기록됨에 주의하자.
심층 분석Deep Profile 기본 프로파일러는 Awake (), Start(), Update (), FixedUpdate()와
●
같은 호출(혹은 콜백Callback) 함수03에 의해 만들어진 메모리 할당과 시간만을 기록한다. 반대로 심층 분석에서는 사용된 모든 함수의 메모리 할당과 시간을 기록하도록 스크립트를 다시 컴 파일한다. 따라서 기본 상태보다 더 많은 하드웨어 성능을 요구한다. 특히 메모리 요구량이 급격히 증가하는데, 프로젝트 규모나 컴퓨터 성능에 따라서는 심층 분석을 실행하는 것만으 로 메모리 부족 현상이 발생할 수 있다.04
TIP
심층 분석을 하면 전체 프로젝트가 다시 컴파일된다. 따라서 이미 실행 중인 프로젝트를 프로파일링 할 때는 이 옵션을 켜고, 끄지 않는 것이 좋다.
심층 분석 옵션은 코드 전체에 사용되는 함수를 일괄 측정하기 때문에 대부분의 프로파일링에는 적합하지 않다. 기본 프로파일링이 충분히 자세하지 않은 경우, 아주 작은 테스트 장면을 프로파일링 하는 경우, 게임의 일부 기능만을 따로 프로 파일링 하는 제한된 경우에 적합하다. 심층 분석으로 일반적인 크기의 장면이나 좀 더 큰 프로젝트를 정밀 분석하기에는 여러 문제가 있다. 이런 경우에는 차후 설 명할 목표 코드 프로파일링Targeted Profiling of code segments을 이용하는 게 좋다.
에디터 프로파일링Profile Editor 에디터 자체를 프로파일링 하는 기능이다. 자체 제작한 스크
●
립트를 분석할 때 유용하다.05
TIP
에디터 프로파일링은 액티브 프로파일러Active Profiler를 에디터 옵션으로 설정해야만 이용할 수 있다.
03 옮긴이주_ 메서드가 멤버 함수를 의미할 때는 이후에도 함수라고 표기했다. 04 옮긴이주_ 낮은 사양의 컴퓨터에서 심층 분석을 꼭 해야 한다면 컴퓨터의 가상 메모리를 강제로 추가 할당하 는 게 도움이 된다. 05 옮긴이주_ 아무런 프로젝트를 걸지 않아도 프로파일링이 되므로 프로파일러가 어떻게 동작하는지 살펴보기에 좋다.
1장 성능 문제 발견하기
_ 27
액티브 프로파일러Active Profiler 에디터 자체 또는 같은 컴퓨터에서 실행 중인 유니티 프로
●
그램, 외부 기기에서 실행 중인 유니티 애플리케이션 중 무엇을 분석할지 선택할 수 있다. 지우기Clear 타임라인에 남은 모든 프로파일 기록을 삭제한다.
●
●
프레임 선택Frame Selection 프레임 카운터를 통해 기록을 시작하면 얼마나 많은 프레임을 프 로파일링 했는지 알 수 있다. 한 프레임 앞이나 뒤로 갈 수 있는 버튼이 있고, 마지막 버 튼인 Current를 통해서는 프레임 선택을 취소하고 최신 프레임으로 이동할 수 있다.
Current를 사용하면 분석 영역의 내용이 계속해서 갱신된다.
타임라인 영역 수집한 프로파일링 데이터를 보여준다. 엔진 파트별로 창이 나눠어 있는
●
데 각각의 창에는 세부 구성 요소들을 화면에 표시하거나 감출 수 있는 다양한 색의 사각 형 아이콘이 있다. 이 사각형 아이콘으로 엔진의 분야별 구성 요소를 확인할 수 있다. 특 정한 창을 선택하면 선택한 창의 필수 요소들이 작업 분석 영역Breakdown View에 표시된다. 작업 분석 영역은 어떤 영역을 선택했는가에 따라 다른 정보들을 보여준다.
TIP
각 창은 창의 우측 상단 ‘×’ 표시를 클릭하면 닫을 수 있고, Add Profiler 옵션을 통해서 다시 추가 할 수도 있다.
CPU 영역
CPU 영역에서는 실행 중인 유니티의 다양한 하위 시스템 (MonoBehaviour 객체, 카 메라, 렌더링, 물리 연산, 에디터 인터페이스를 포함한 사용자 인터페이스, 오디오, 프로파일러 자신)의
CPU
사용량을 보여준다. CPU 영역을 선택하면 작업 분석 창은 다음 세 가지 방법으로 CPU 사용량을 표시한다. 계층 구조Hierarchy
●
●
●
원시 계층 구조Raw Hierarchy 타임라인Timeline
계층 구조는 연관된 자료 구조와 전역 유니티 함수 호출 내용을 모아 보여준다. 예 를 들면 렌더링 구분 함수인 BeginGUI ( )와 EndGUI ( )의 호출 내역을 합쳐 보여 주는 식이다.
28 _
원시 계층 구조는 각각의 유니티 전역 함수 호출을 다른 항목으로 표시한다. 같 은 이름의 항목이 여러 번 등장하기 때문에 읽기가 더 어렵지만 특정한 전역 함수 가 호출된 횟수나 어떤 순간에 더 많은 자원을 필요로 하는지를 확인할 수 있다. BeginGUI ( )와 EndGUI ( )의 함수는 호출에 따라 흩어진 상태로 보여주기 때문 에 원하는 정보를 찾기가 더 어렵다. 이 경우 타임라인Timeline 모드가 더 유용할 수 있다 ([그림 1-6]에서 창으로 나눠진 타임라인과 는 다른 작업 분석 영역의 표시 옵션이다).
이 모드는 CPU 사용량을 스택의 연관성과 호출에
따라 그래프로 시각화한다. 가장 위쪽의 직사각형은 유니티 엔진으로부터 직접 호 출받는 Start ( ), Awake ( ), Update ( )와 같은 함수들이다. 그 아래 직사각형은 이 함수들이 호출한 함수들로, 객체와의 연관 관계와 무관하 게 호출 상태를 기준으로 보여준다. 사각형의 넓이는 호출된 함수의 상대적인 처리 시간을 뜻한다. 상대적으로 짧은 처리 시간을 요구하는 함수는 눈에 띄지 않도록 회색 상자로 보인다. CPU 타임라인 모드의 가장 큰 장점은 사각형의 상대적 길이를 통해 한 프레임 동 안 어떤 스택의 호출이 가장 많은 시간을 소모했는지를 직관적으로 알 수 있다는 데 있다. 최소한의 노력으로 문제의 원인을 발견할 수 있다. 예를 들어 [그림 1-7]과 같은 성능 문제에서 회색이 아닌 직사각형 3개는 직사각 형의 길이가 비슷하므로 3개 모두 비슷한 시간에 처리가 완료됐다. 그림 1-7 타임라인 방식의 CPU 분석 창
1장 성능 문제 발견하기
_ 29
여기서 두 가지 결론을 유추할 수 있다. 하나는 개선할 함수가 3개나 되기 때문에 코드를 개선할 여지가 많다는 점이고, 다른 하나는 직사각형 3개의 길이가 비슷하 므로 하나의 함수를 개선한다고 해서 성능 개선 효과가 크지 않을 것이란 사실이 다. 결국, 3개 함수를 모두 개선해야 현재 프레임의 처리 시간을 줄일 수 있다. 이 렇듯 CPU 분석 창은 다음 장에서 살펴볼 스크립트 전략 수립에 매우 유용한 정보 를 제공한다. GPU 영역
GPU 영역은 GPU에 의해 호출되는 함수들을 보여준다는 점을 제외하면 CPU 영역과 크게 다르지 않다. GPU에서 호출하는 유니티 함수는 주로 카메라, 그리 기, 불투명도, 투명도, 지형, 광원, 그림자 등이다. GPU 영역의 정보들은 ‘6장 역 동적인 그래픽’에서 주요하게 다룬다. 렌더링 영역
렌더링 영역은 Setpass calls06 횟수, 화면에 나타난 배치Batch07수, 동적 배치와 정적 배치로 발생한 배치의 수, 텍스처에 소모된 메모리양 등 렌더링 관련 통계를 보여준다. 렌더링 영역은 ‘3장 배칭의 유용성’에서 주로 다룬다.08 메모리 영역
메모리 영역은 단어의 뜻 그대로 메모리를 보여준다. 메모리 영역을 선택하면 작 업 분석 영역을 다음 두 모드로 바꿀 수 있다.
06 옮긴이주_ 몇 번 그리기 요청을 보냈는가를 의미한다. 1MB 파일 1,000개 복사, 1GB 파일 하나 복사와 같은 식이다. 07 옮긴이주_ CPU에서 GPU로 넘어가는 폴리곤 덩어리 수를 의미한다. 배치가 완료돼야 SetPass call이 가능 하다. 08 옮긴이주_ 엄밀히 말하자면 SetPass call+배치는 드로우 콜Draw Call이다. 유니티 4까지는 드로우 콜이 공식 용어로 쓰였으나 유니티 5부터는 드로우 콜을 SetPass call과 배치로 나눠 표현하도록 유도하고 있다. 하지 만 아직도 대부분의 경우 둘의 수는 정확히 일치한다.
30 _
단순 모드Simple Mode
●
●
정밀 모드Detailed Mode
단순 모드에서는 유니티 엔진 하부 구성 요소의 메모리 사용량 전반을 보여준다. 가 비지 컬렉터가 활동하며 정리할 힙의 크기, 프로파일러로 수집된 데이터를 보관하 기 위한 메모리의 크기, 그래픽, 오디오(FMOD, 오디오 미들웨어) 등이다. 정밀 모드에서는 각 객체와 모든 구성 요소, 자체적으로 요구하는 메모리와 관리하 는 메모리 크기를 모두 보여준다. 객체가 메모리를 차지한 이유와 언제 할당되고 해 제될지도 알 수 있다. 메모리 영역은 ‘7장 메모리 관리의 주인’에서 자세히 다룬다. 오디오 영역
오디오 영역은 오디오를 재생하거나 정지할 때 오디오 프로그램과 음원 파일에 필 요한 자원 등 오디오를 위한 CPU와 메모리 사용량을 보여준다. 오디오 영역은 ‘4 장 당신의 아트 자원을 활용하라’에서 자세히 살펴본다. 성능을 최적화할 때 보통 오디오에는 크게 신경을 쓰지 않는다. 하지만 제대로 처 리하지 않은 오디오는 병목현상의 주된 원인이 된다. 따라서 개발 중에 오디오 시 스템의 메모리와 CPU의 요구량을 미리 확인하는 습관을 들이는 것이 좋다. 3D/2D 물리 영역
물리 영역은 엔비디아Nvidia의 피직스PhysX를 기반으로 한 3D 물리 영역과 Box2D 기반의 2D 물리 영역이 있는데 물리 객체, 접촉된 횟수 (3D, 2D 공통), 충돌체 (2D만) 등 의 정보를 보여준다. 이 두 영역은 ‘5장 더 빠른 물리’에서 상세히 설명한다. 집필 당시의 유니티 버전은 v5.2.2f1이었는데, 3D 물리 영역에서는 몇 개 되지 않는 정보만 표시하는 반면, 2D 물리 영역에서는 더 다양한 정보를 보여줬다.
1장 성능 문제 발견하기
_ 31
1.2
성능 분석을 잘 하는 법
보통 좋은 코드일수록, 좋은 프로젝트일수록 문제점을 쉽게 발견할 수 있다. 문제 는 어떻게 고치는 가다. 예를 들어 함수가 단 하나의 거대한 for 구문을 실행한다 면 루프의 반복 실행 자체 또는 루프 내부에서 성능 문제가 발생한다고 볼 수 있 다. 안타깝게도 개인 개발자가 아닌 이상 다양한 사람이 프로그램 개발에 참여하 다 보면 언제나 깔끔하고 같은 코딩 스타일을 유지할 수는 없다. 문제 수정에 사용 한 ‘땜빵’이나 해커 스타일의 단순 무식한 코드를 처음부터 세련되게 수정할 시간 도 없다. 그러므로 이상한 코드를 분석, 개선하는 일은 개발자에게 필연이라고 할 수 있다. 많은 개발자는 성능 최적화가 문제 해결의 한 방식임을 때때로 잊는다. 프로파일 러를 사용해 데이터를 분석하는 일은 문제가 어디에서 발생하는지, 얼마나 심각한 문제인지를 찾는 것이다. 작지만 중요한 단서를 무시하거나 잘못된 데이터 탓에 잘못된 결론을 짓는다면 많은 시간과 노력을 낭비하게 된다. 이 대부분은 디버깅 과 마찬가지로 “문제는 매우 기술적이고 복잡할 거야”라는 마음으로 프로파일링 을 하기 때문이다. 다음 대조표는 ‘유령’ 같은 문제를 추적하는 데 드는 시간과 노력을 낭비하지 않도 록 도와준다. 물론 각각의 프로젝트는 전혀 다른 고려가 필요하고 디자인 패턴 또 한 제각각이다. 이 대조표는 유니티 프로젝트의 다양성을 충분히 고려해 설계됐다. 스크립트는 의도대로 현재 씬Scene에 있는가?
●
●
●
●
●
스크립트는 계획된 횟수만큼 현재 씬에 적용했는가? 프로파일링 중에는 최대한 코드를 수정하지 않았는가? 내부 방해 요인을 최소화했는가? 외부 방해 요인을 최소화했는가?
32 _
1.2.1 스크립트 존재 여부 확인 인간의 뇌는 패턴 인식에 매우 뛰어난 능력을 발휘한다. 그래서 있어야 할 것이 없 는 것을 찾는 일은 쉽지만, 무언가가 없다는 사실을 발견하기는 어렵다. 뇌는 ‘형 태 재인Pattern Recognition’의 인식 방법을 통해 없는 부분을 자동으로 처리한다. 유니 티로 개발할 때 이러한 일이 흔히 일어나는 순간은 의도된 스크립트의 존재 여부 를 확인할 때다. 의도한 스크립트가 현재 씬에 제대로 있는지 확인하는 일은 단순 하기 때문에 흘려버리기 쉽다. 스크립트 호출 여부를 확인하는 것은 시간과 노력 을 절약하는 가장 쉽고 중요한 과정이다. 계층 구조 창의 텍스트 박스에서 다음 명령어를 실행하면 스크립트 존재 여부를 빠르게 확인할 수 있다. t:
예컨대 [그림 1-8]과 같이 계층 구조의 텍스트 입력 창에 t:mytestmonobehaviour 라고 입력하면(주의! 대소문자 구분을 하지 않는다) MyTestMonobehaviour를 구성 요소로 가진 모든 게임 객체 목록이 나타난다.
TIP
이 객체 목록은 스크립트 명에 입력한 단어들이 있는 모든 게임 객체를 검색해 보여준다.09
09 옮긴이주_ ‘Apple’이라고 입력하면 ‘apple1’, ‘applepie’란 이름의 스크립트가 연결된 모든 객체가 나타나 는 식이다.
1장 성능 문제 발견하기
_ 33
그림 1-8 스크립트 존재 여부 확인
이전 테스트나 다른 사람에 의해 이미 정지된 게임 객체도 주어진 이름의 스크립 트와 연결돼 있으면 목록에 나타난다.
1.2.2 스크립트 실행 횟수 확인 가령 MonoBehaviour 함수가 문제를 일으켰고 씬에서 단 한 번만 나타난다면 함 수 중복 호출로 인해 충돌이 일어날 리 없다고 생각하기 쉽다. 이는 위험한 생각이 다. 누군가 똑같은 객체를 여러 번 만들었을 수도 있고, 똑같은 객체의 인스턴트를 여러 번 썼을 수도 있다. 때로는 똑같은 함수를 중복 호출한 것이 과도한 자원을 소모하는 원인일 수도 있다. 따라서 미리 작성한 검색 목록을 확인할 때는 중복된 함수 호출도 확인해야 한다. 씬에 단 한 번만 등장하는 구성 요소가 검색 목록에 여러 번 나온다면 병목현상에 대한 기존 가설을 폐기해야 한다. 이러한 문제를 해결하기 위해서는 초기화 코드 를 수정해 이런 일이 다시 일어나지 않도록 하거나 에디터에서 자체 도움말을 적 어 다른 개발자와 기획자가 같은 실수를 하지 않도록 알려야 한다.
34 _
생산성을 높이려면 이러한 단순 실수를 줄여야 한다. 무언가를 하지 말라고 강력 하게 제지하지 않는 이상 누군가는 언젠가, 어디선가, 어떤 이유에서든 하기 마련 이고, 이로 인한 문제 해결에 많은 시간과 노력이 들기 때문이다.
1.2.3 프로파일링 중 코드 변환하지 않기 사람의 기억력에는 한계가 있다. 그러므로 문제의 원인을 찾고자 프로파일링을 하 는 동안에는 코드를 변경하지 않는 것이 좋다. 문제를 분석하는 동안 디버깅 용도 로 로그 생성 코드를 넣어야 할 때가 있다. 코드를 추가하려면 필요한 코드를 생각 하고 입력한 다음 다시 컴파일해야 한다. 프로파일링이 끝난 후에는 추가한 코드 를 제거해야 한다. 만일 프로파일링이 끝난 후 코드 제거를 깜박한다면 최종 배포 버전은 디버그 로그를 기록하는 데 무의미한 자원을 소모할 것이다. 이러한 실수를 예방하는 가장 좋은 방법은 버전 컨트롤 툴을 이용하는 것이다. 코 드를 추가할 때마다 파일의 버전을 바꾸고 작업이 끝난 후에는 원래 버전으로 되 돌리는 것이다. 그러면 최종 배포 버전에 불필요한 코드가 삽입되는 것을 막을 수 있다. 디버깅 시 중단점을 이용하는 것도 한 방법이다. 중단점을 이용하면 코드 변경과 재컴파일로 인한 시간 낭비가 없고 전체 호출 스택을 확인할 수도 있다. 이뿐만 아 니라 변수의 데이터, if-else 구분 등의 코드 분기점도 쉽게 살펴볼 수 있다.
1.2.4 내부 방해 요인 줄이기 유니티 에디터 자체에는 개발자들을 헷갈리게 하는 애매한 점들이 있다. 한 프레임에 많은 연산이 필요해 눈에 띄게 화면이 정지되고 프로파일러가 결과물 을 제대로 기록하지 못하는 경우가 그러하다. 게임이나 장면의 초기화 과정에서 이러한 현상은 특히 불편한데, 다음 절인 ‘자체 CPU 프로파일링Custom CPU Profiling’ 에서 이 문제를 해결할 수 있는 프로파일러 대체 기술을 살펴본다.
1장 성능 문제 발견하기
_ 35
또 많은 개발자가 흔히 하는 실수는 (필자가 이 책을 쓰는 동안 몇 번이고 저지른 실수다) 프로파 일러를 조작할 때 에디터의 게임 창Game Window을 미리 클릭해 선택하지 않는 것이 다. 프로파일러 창이 가장 마지막으로 클릭했거나 단축키로 조작한 창이라면 에디 터는 이후 키보드 입력을 프로파일러에게 보내므로 게임 속 객체들이 키보드 입력 에 동작하지 않는다. 수직 동기화VSync: Vertical Sync는 프로그램의 프레임을 출력 기기의 주파수에 강제로 맞추는 기능이다. 이 기능을 활성화하면 프로그램은 모니터의 재생속도에 맞춰 프 레임 연산 속도를 늦춘다. 그러면 WaitForTargetFPS라는 대기 시간이 CPU 사 용량을 점령한다. 따라서 프로파일러상에서는 똑같은 이름의 잡동사니 항목이 대 량으로 보여 문제 원인을 찾기가 어려워진다. 프로파일러는 호출에 대한 각각의 상대적인 CPU 점유율을 보여주는데, VSync 는 이러한 항목들을 가릴 수 있다. 따라서 성능을 테스트할 때는 Edit → Project Settings → Quality → currently selected build platform에서 VSync를 꺼 야 한다. 간과하기 쉬운 것은 에디터 콘솔에 뜨는 다양한 예외 처리와 경고 메시지 다. Debug.Log ( )를 비롯한 Debug.LogError ( ), Debug.LogWarning ( ) 같은 함수들은 매우 많은 CPU와 메모리 (힙) 자원을 요구한다. 게다가 사용하고 난 메모 리 공간을 회수하는 데에도 CPU 자원이 많이 쓰인다. 대부분 오류는 컴파일러와 잘못된 객체 속성으로 인해 발생한다. 이러한 문제는 에디터를 이용할 때 눈에 잘 띄지 않고 프로그램을 실행할 때 여러 문제를 일으킨 다. 예를 들어 Update ( ) 함수에서 필요로 하는 객체의 원형을 에디터에서 설정 하지 않으면, Update ( )가 실행될 때마다 MonoBehaviour는 예외 처리를 요청 한다. 이러한 예외 처리는 프로파일링을 통해 문제를 찾기 어렵게 만든다. [그림 1-9]처럼 경고나 알림 표시를 꺼도 CPU와 메모리 자원 소모가 사라지는 것은 아니다. 따라서 모든 경고를 표시하도록 하고 발생한 경고는 모두 해결하는 것이 좋다. 36 _
그림 1-9 경고와 알림 표시
1.2.5 외부 방해 요소 줄이기 여기서 살펴본 것들은 간단하지만 필수적인 작업이다. 테스트할 때는 CPU 혹은 메모리를 점유하고 있는 기타 프로세스를 최대한 줄이는 것이 대표적이다. 이러한 메모리가 부족하면 페이지 파일 스왑Swap이 많이 일어나 테스트 프로그램이 더욱 느려지므로 주의해야 한다.
1.3
코드 삽입을 통한 목표 프로파일링
앞서 작성한 대조표로도 문제가 해결되지 않는다면 더 상세한 분석이 필요한 진 짜 문제가 있다. 진짜 문제를 해결하려면 좀 더 정밀한 진단이 필요하다. 프로파 일러 창은 성능의 전반적인 부분을 보여준다. 그 덕분에 어떤 프레임에서 어떤 MonoBehaviour와 함수가 문제를 일으키는지 재빨리 알 수 있다. 때로는 좀 더 상 세한 심층 분석이 필요하다. 문제를 재생할 수 있는지, 어떤 상황에서 병목현상이 발생하는지, 코드의 어느 부분이 문제인지 찾아야 하는 경우다. 심층 분석을 하려면 코드의 일정한 부분을 프로파일링 해야 한다. 지금부터 더 세 밀한 분석을 도와줄 기술을 소개하겠다. 유니티에서는 이러한 기술들을 다음 두 가지 범주로 분류한다. 스크립트에 코드를 넣어 프로파일러 쓰기
●
●
더 정밀한 시간 분석과 로그 기록
NOTE
이 절에서는 C# 코드를 이용해 스크립트의 병목현상을 발견하는 과정을 살펴본다. 유니티의 다른 부 분에서 발생한 병목현상을 찾는 방법은 다른 장에서 다룬다.
1장 성능 문제 발견하기
_ 37
1.3.1 프로파일러 스크립트 넣기 전역 프로파일러 클래스를 이용하면 프로파일러를 스크립트에 넣을 수 있다. 유 니티 개발 문서에 소개된 다양한 프로파일러 함수 중 가장 중요한 함수는 프로파 일러를 활성화 또는 비활성화하는 Profiler.BeginSample ( )과 Profiler. EndSample ( )이다.
TIP
개발자용 함수인 BeginSample ( )과 EndSample ( )은 C++ 디버깅 모드의 assert ( ) 함수처럼 개 발자용으로 빌드된 프로그램에서만 컴파일된다. 따라서 일반 배포용 버전에서는 어떠한 자원 낭비도 일으키지 않는다. 나중에라도 프로파일링을 다시 해야 할 경우 이들 함수는 코드에 남겨두어도 된다.
BeginSample ( ) 함수는 CPU 사용량 영역의 계층 모드 속 인스턴스를 새로운 이름으로 오버로딩한다. 예를 들어 다음 코드는 계층 모드를 자체 이름 (My Profiler Sample)으로 분석 창에 호출하는 예다.
void DoSomethingCompletelyStupid() { Profiler.BeginSample("My Profiler Sample"); List listOfInts = new List(); for (int i = 0; i < 1000000; ++i) { listOfInts.Add(i); }
Profiler.EndSample(); }
실행 결과는 [그림 1-10]과 같다. 아무 쓸모도 없는 정수 100만 개를 목록에 넣 은, 아무것도 하지 않는 멍청한 함수로 인해 CPU 사용량이 치솟고 몇 메가의 메 모리가 사용되고 있다. 이러한 자원 사용량은 ‘My Profiler Sample’이라는 이 름으로 프로파일러 분석 창에 보여준다.
38 _
그림 1-10 BeginSample ( ) 함수 예
심층 분석 모드에서는 [그림 1-11] 같이 임의로 넣은 자체 이름의 함수 호출 내역 이 계층의 최상단에 나타나지 않는다. 그림 1-11 심층 분석 모드 예
1장 성능 문제 발견하기
_ 39
계층의 최상단에 자체 이름의 함수가 나타나지 않는 이유는 알려지지 않았다. 그 렇다고 자체 이름의 함수가 없는 것은 아니므로 계층 구조 모드를 이용해 정밀 분 석을 할 때는 함수 이름을 천천히 잘 찾아봐야 한다.
1.3.2 자체 CPU 프로파일링 유니티에 포함된 프로파일러는 문제 발견을 위한 툴 중 하나일 뿐이다. 때로는 자 체 프로파일링을 통해 정보를 얻어야 할 때도 있다. 유니티 프로파일러가 충분한 정보를 제공하지 않는 경우, 프로파일러의 추가 자원 소모가 너무 큰 경우, 그저 모든 것을 직접 해보고 싶은 경우가 여기에 해당한다. 이유를 불문하고 코드를 추 가해 직접 분석하는 것 자체가 나쁠 것은 없다. 언제까지나 유니티만으로 개발하 진 않을 테니까. 프로파일링 툴은 매우 복잡하다. 짧은 시간에 유니티 프로파일러와 견줄 만한 프 로파일링 프로그램을 만드는 것은 거의 불가능하다. 하지만 CPU 사용량을 알 고 싶다면 정밀한 타이머 시스템, 저렴한 로그 기록 시스템, 테스트 코드만으로 시작할 수 있다. 닷넷.NET 라이브러리의 System.Diagnostics 네임스페이스 하 위에는 Stopwatch라는 클래스가 있다 (엄밀히 말하면 모노 프레임워크에 속한다). 이 Stopwatch 클래스를 이용하면 언제라도 시간을 측정할 수 있다. 불행하게도 이 클래스는 측정 시간이 정확하지 않다. 이 클래스는 약 1,000분의 1초에서 최대 1만 분의 1초 정도의 정확도라서 CPU 주파수를 매우 정밀하게 측 정할 수 없다. 정확성에 관한 이야기는 잠시 뒤로 미루어두고 Stopwatch 클래스 를 최대한 이용하는 방법을 알아보자. 이러한 초정밀 측정이 왜 필요한지부터 생각해보자. 대부분 게임은 초당 30프레임에서 60프레임으로 그래픽 프레임을 처리한다. 이 는 한 프레임을 0.033초나 0.016초 이내에 연산해야 한다는 뜻이다. 만약 성능
40 _
개선의 목표가 0.01초라면 100만 분의 1초 단위의 정확성은 사실상 큰 의미가 없다. 이와 반대로 매우 정밀한 측정이 필요한 경우도 있다. 그럴 땐 반복 수행에 걸린 전체 시간을 측정된 시간으로 나누면 정밀도를 높일 수 있을 것이다. 다음은 일정 횟수로 테스트한 전체 시간을 Stopwatch 클래스로 측정하는 타이 머-반복 테스트 예제다. using using using using
UnityEngine; System; System.Diagnostics; System.Collections;
public class CustomTimer : IDisposable { private string m_timerName; private int m_numTests; private Stopwatch m_watch; // 타이머에 이름과 전체 테스트 횟수를 입력한다.
public CustomTimer(string timerName, int numTests) { m_timerName = timerName; m_numTests = numTests; if (m_numTests