어느 프로젝트건 함수 내부에서 연산을 위해 함수 내부에서 리스트 컨테이너를 선언하고 조작하는 일은 빈번하다.
특히나 렌더링 쪽에서는 자주 사용되기 때문에
이미 내부적으로는 연산 컨테이너로서 리스트 풀을 사용하고 있다고 한다.
때문에 크기가 큰 컨테이너를 지역변수로 자주 사용하는 경우 멤버 변수로 컨테이너를 선언해 놓고서 사용하라고 권장한다.
하지만 멤버변수가 특성상 상황에 따라 메모리에 계속 물고있게 되는 경우도 있으며 보통 멤버변수는 상단에 위치하기 때문에 가독성 측면에서도 좋지 않다.
그래서 정적 리스트를 오브젝트 풀링 하듯 풀링 하는 방식이 리스트 풀이다.
일부 Astar Pathfinding Project 같은 에셋들을 뜯어보면 자체적으로 리스트 풀을 만들기도 했는데
이번에 유니티 2021부터 공식 지원하기 시작했다.
유니티 공식문서 : ListPool<T0>
https://docs.unity3d.com/ScriptReference/Pool.ListPool_1.html
사용법은 놀라울 정도로 간단한데
Get해서 사용하고 사용이 끝나면 Release하면 끝이다.
테스트해보기
리스트 풀이 정말로 작동한다면 GC Alloc이 낮게 측정되어야 한다.
이를 프로파일러 마커를 사용하여 체크해 보자.
리스트 길이 1만 개를 1만 번 반복해 봤다.
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
public class ListPoolTest : MonoBehaviour
{
const int repeatCount = 10000;
const int listLength = 10000;
static readonly ProfilerMarker s_Marker_notUse = new("ListPool_NotUse");
static readonly ProfilerMarker s_Marker = new("ListPool_Use");
void Start()
{
using (s_Marker_notUse.Auto())
{
for (int repeat = 0; repeat < repeatCount; repeat++)
{
var list = new List<int>(listLength);
for (int i = 0; i < listLength; i++)
list.Add(i);
int result = 0;
for (int i = 0; i < listLength; i++)
result += list[i];
UnityEngine.Debug.Log($"일반리스트 사용 : {result}");
}
}
using (s_Marker.Auto())
{
for (int repeat = 0; repeat < repeatCount; repeat++)
{
var list = UnityEngine.Pool.ListPool<int>.Get();
for (int i = 0; i < listLength; i++)
list.Add(i);
int result = 0;
for (int i = 0; i < listLength; i++)
result += list[i];
UnityEngine.Debug.Log($"리스트풀 사용 : {result}");
UnityEngine.Pool.ListPool<int>.Release(list);
}
}
}
}
결과
일반 리스트 new 사용 : 387 MB GCAlloc
리스트 풀 사용 : 7.6MB GCAlloc
리스트 풀 쪽이 확연하게 GC가 줄어들었다.
마치며..
List 풀 외에도 다른 타입들도 존재한다.
마지막 UnsafeGenericPool는 컬렉션 검사를 수행하지 않는다고 함.
주의점
상황에 따라서는 Get()해 온 리스트를 리턴 시켜서 다른 함수에 전달해서 사용하다가
나중에 Release 해야 되는 경우도 존재한다.
하지만 그 경우 Release를 실수로 까먹으면 메모리가 줄줄 새니 조심하도록 하자.
또한 여느 풀링이 그렇듯 스레드 세이프하지 않다!
GenericPool의 경우 Relase할 때 내용을 비우지 않는다.
HashSet등을 GenericPool로 관리해보면 Get()으로 가져올 때 이전 데이터가 남아있다!
ListPool, HashSetPool 등은 이 데이터를 지우는 부분이 추가된 하위 변형이라고 생각하면 된다.
'🌍 Unity > 최적화' 카테고리의 다른 글
URP PostProcess FilmGrain Texture int build size (0) | 2021.12.16 |
---|---|
영상 필기 - [유니티 TIPS] 모바일 게임 성능 최적화 (0) | 2021.10.16 |
스프라이트 아틀라스를 사용할 때는 이중압축에 주의하세요 (0) | 2021.07.25 |
스프라이트 아틀라스를 사용할때는 리소스 폴더를 주의하세요 (0) | 2021.07.25 |
Crunched 압축.. (0) | 2021.07.25 |