뒤로
민서
민서 ·

생성/파괴 vs Object Pooling

들어가며

Instantiate와 Destroy는 Unity에서 제공하는 오브젝트 생성, 파괴를 담당하는 코드입니다. Object Pooling은 이러한 생성과 파괴 비용이 비싸기 때문에 대안으로 나온 최적화 기법입니다. Object Pooling의 경우는 자료구조 Queue만 알고 있어도 원리를 알고 사용할 수 있다는 장점 덕에 Unity를 입문하시는 분들이 가장 먼저 배우는 최적화 기법이기도 합니다. 그래서 저는 저번 주에 이 두 가지 방식을 직접 구현해보고 비교해봤습니다. 근데 생각치도 못한 난관에 생각보다 오래 걸려서 어제 작성해야 했는데 오늘까지 분석해보고 이제서야 결과를 내게 되네요. 지금까지의 생각보다 험난했던 과정을 여러분들께 소개해드리도록 하겠습니다.

Object Pooling이 나오게 된 배경

C#의 주목할만한 특징 몇 가지 중에 재밌는 특징이 있습니다. C#은 Garbage Collector라는 친구가 있어서 아무도 안쓰는 메모리가 있다면 알아서 수거해가는 친구인데요, 이 덕분에 C++ 처럼 메모리 관리를 안해줘도 괜찮다는 장점이 있습니다. 하지만 단점은 개발자가 메모리 관리를 할 수 없어요(?). Garbage Collector가 작동하는 방식은 프로그램을 멈추고 메모리를 회수한 뒤에 다시 프로그램을 재개하기 때문에 멈추고 재개하는 그 과정에서 프레임 드랍이 일어납니다. 소위 랙이라고 불리는 현상이 나타나죠. 그렇기 때문에 개발자들은 가비지가 안나오게끔 코드를 짜는 방식으로 발전해옵니다. 메모리 관리를 하지 않게 하기 위해 Garbage Collector를 만들었는데 정작 개발자들은 Garbage Collector가 사용되지 않게끔 코드를 짜는 아이러니한 일이 일어나고 있습니다. 그 중 하나가 Object Pooling입니다. Object Pooling은 간단합니다. 오브젝트를 제거할 때 이러한 가비지가 나오는 것이 거의 확정이기 때문에 미리 만들어두고 Queue에 넣어둔 뒤 필요할 때 꺼냈다가 삭제하는 것이 아닌 다시 Queue로 넣어 재사용한다는 목적으로 만든 최적화 기법이예요.

직접 구현해서 비교해보자

image.pngimage.png

생각보다 gif 파일 용량이 커서 사진으로 대체하겠습니다

생성/파괴의 경우 최저 프레임이 28프레임이 나왔고

Object Pooling의 경우 최저 프레임이 35프레임이 나왔습니다.

Garbage Collector(황토색?)와 Script(파란색)의 그래프만 뜨도록 표시를 했는데 분명 차이가 있어야 하는데 차이가 없어서 당황했습니다.

image.png

차이가 있긴 하지만 이대로 끝내긴 너무 찝찝했습니다. 생각했던 것보다 Instantiate/Destroy에서의 Garbage Collector가 활발하지 않았고 성능 차이도 10프레임 정도의 차이 밖에 안났던 것입니다.

그래서 선배께 도움을 청해 제 코드가 잘못된 것인지 확인해주실 수 있는지 요청을 드렸고 결과는 비슷했습니다.

image.png

솔직히 아무 의심도 없이 당연히 무조건 써야한다, 좋다고만 생각했던 Object Pooling과의 성능 차이가 얼마 나지 않아 뒤통수를 쌔게 맞은 느낌이 들었습니다.

그래서 결국 의미는 없었는가

선배와 제가 그때 당시 내린 결론은 Unity 2022 에서 제공하는 Instantiate와 Destroy의 코드는 최적화를 잘해서인지 그렇게 비용이 비싸지 않았던 것 같다. 그래서 Unity 2017 버전에서 돌리면 유의미한 차이가 있을 것이다 였습니다. 그리고나서 계속해서 테스트를 해본 결과

image.png

드디어 납득이 갈만한 결과가 나왔습니다. 저희가 테스트했던 환경에서는 생성했던 객체(총알)이 그저 앞으로 나가는 기능밖에 없었기 때문에 별 차이가 없었으나 실제 게임은 다릅니다. 이것보다 오브젝트 하나하나가 훨씬 무겁겠죠.

결론은 생성되는 오브젝트가 무거우면 무거울수록 Object Pooling에 의미를 갖게 되는 것 이였습니다.

오브젝트가 생성할 때 실행되는 초기화 코드(Start() 함수, Awake() 함수)가 계속 실행되느냐 vs 한번 실행되느냐 의 차이는 컸기 때문입니다.

마무리하며

사실 원래 여기에 DOTS(Data Oriented Technology Stack) 방식으로 구현한 탄막 게임까지 같이 비교하여 포트폴리오를 작성하려고 했으나 생각지도 못했던 부분에서 오래걸리기도 하였고, DOTS 방식의 코딩 스타일이 기존의 객체 지향 프로그래밍 방식과 전혀 달라 일주일 내로는 무리였습니다. 하지만 당연하게 사용해왔던 이러한 최적화 기법을 직접 비교해보고 관찰함의 과정을 통해 더 유의미한 시간을 보낼 수 있었던 것 같습니다. 이 에피소드는 앞으로도 기억에 남을 것 같네요 재밌었습니다. 다음주에는 혹은 다다음주에는 DOTS를 구현하고 찾아뵙도록 하겠습니다.

주니어 메이커들 그룹의 글
4

댓글

로그인 후 댓글을 남길 수 있습니다.

나영서
나영서

저는 백엔드를 공부하고 있어서 유니티는 잘 모르지만 그런 저도 이해될 정도로 잘 작성하셨네요🙆🏻‍♀️ 게임에서는 프레임이 중요하기 때문에 이런 최적화를 사용하기도 하는군요, 새로운 내용 배워갑니다. 앞으로도 좋은 글 많이 공유해주세요!

민서
민서

최대한 쉽게 적으려고 했으나 실제로 어떨지 몰라 많이 걱정했는데 너무너무 다행이네요. 부족한 글 읽어주셔서 감사합니다!