[디자인 패턴]#15_객체 풀, Object Pooling

2022. 11. 2. 10:55· 언어/디자인 패턴
목차
  1.  
  2. [디자인 패턴] #15_객체 풀, Object Pooling

 

[디자인 패턴] #15_객체 풀, Object Pooling

게임 디자인 패턴 중 "최적화 패턴"에 대해 알아보겠습니다.

"게임 프로그래밍 패턴"의 19 항목, "객체 풀"에 해당하는 내용입니다.

 

 


 

개념
런타임 중 객체의 할당과 해제를 반복하지 않고, 고정 크기 "풀"에 할당된 객체를 재사용함으로써 메모리 사용 성능을 개선합니다!

 

왜 필요할까?

1. 메모리 단편화, Memory Fragmentation

출저: 게임 프로그래밍 패턴 362pg

  • 메모리 단편화란, 힙에 사용 가능한 공간이 여유로운 크기로 뭉쳐 있지 않고, 작게 조각나 있는 상태를 의미합니다
  • 전체적으로 사용 가능한 메모리 공간이 충분함에도 불구하고, 연속해서 사용 가능한 영역은 작을 수 있습니다.
  • 이때, 메모리 단편화 문제 + 할당/해제 속도 -> 게임 성능을 저하시키는 요인들을 방지하기 위해 객체 풀이 필요합니다

 

객체 풀

1. Overview

  • 재사용 가능한 객체들을 모아놓은 객체 풀 클래스를 정의합니다.
  • 객체 풀에 존재하는 객체들은 각각 "사용 가능 여부"를 알려줄 방법이 필요합니다.
  • 따라서, 객체 풀의 초기화는 사용할 객체들을 미리 할당받고, "사용 가능함" 상태로 초기화합니다. 
  • 객체가 필요할 때, 객체 풀에 요청하여 "사용 가능함" 상태의 객체를 반환 받고, 해당 객체를 "사용 불가능"상태로 변경합니다.
  • 물론, 해당 객체를 모두 사용하고, 필요 없어지면 "사용 가능함"상태로 돌려놓습니다!

 

2. 언제 사용할까?

  1. 특정 객체를 높은 빈도로 생성/삭제해야 할 때!
  2. 객체들의 크기가 비슷할 때
  3. 객체를 힙에 생성할 때 성능 저하 혹은 메모리 단편화가 우려될 때

 

3. 사용 시 주의할 점

  1. 객체 풀에서 사용되지 않는 객체는 메모리 낭비입니다!
  2. 한 번에 사용 가능한 객체 개수가 고정되어 있습니다!
  3. 풀에 들어가는 객체들의 자료형이 다르다면, 크기가 가장 큰 자료형에 맞춰야 합니다!
  4. 재사용되는 객체는 이전 상태의 값들이 들어있으며, 객체 재사용 시 객체의 완전한 초기화가 필수적입니다.
  5. 사용 중이지 않은, 즉 "사용 가능함"상태의 객체도 메모리 공간을 차지하고 있습니다!

 

  예제 코드

1. Overview

게임 월드 내에서 활용될 이펙트(Particle System) 클래스들은 생성/삭제가 빈번합니다. 파티클 클래스를 예제로 활용하여 "객체 풀" 패턴에 대해서 알아보겠습니다. 

 

2. Particle 클래스

class Particle
{
public:
	Particle() : framesLeft_(0){}

	// 1. 초기화 : LifeTime을 할당받습니다.
	void Init(int lifeTime) { framesLeft_ = lifeTime; }
	// 2. 애니메이션 렌더링 함수
	void Animate()
        {
            if(IsUsing() != true)
                return;

            --framesLeft_;
        }
	// 3. 사용 가능 여부 : LifeTime을 할당받고, 사용 중이면 false를 반환합니다.
	bool IsUsing() const { return framesLeft_ > 0; }

private:
	// 객체의 "사용 가능함" 상태 값
	int framesLeft_;
};

 

2. ParticlePool.h (Object Pool 클래스 선언)

class ParticlePool
{
public:
	// 1. "사용 가능함" 상태의 객체를 초기화합니다.
	void Create(int lifeTime)
	{
		for (int i = 0; i < POOL_SIZE; i++) 
		{
			if (particles_[i].IsUsing() != true)
			{
				particles_[i].Init(lifeTime);
				break;
			}
		}
	}

	// 2. 매 프레임마다 호출되는 애니메이션 렌더링 함수
	void Animate()
	{
		for (auto particle_ : particles_)
			particle_.Animate();
	}

private:
	// 3. 고정 크기의 객체 풀 사이즈
	static const int POOL_SIZE = 100;

	// 4. 객체 풀
	Particle particles_[POOL_SIZE];
};

 

문제 해결

1. 위 코드의 문제점

  • 위 코드의 경우, "사용 가능함" 상태의 파티클 객체를 찾을 때까지 객체 풀을 순회합니다.
  • 객체 풀의 크기가 커질수록, 탐색 성능에 필요한 비용이 커지는 문제가 발생합니다.
  • 이때, 우리는 "빈칸 리스트"를 활용하여 위 문제를 해결할 수 있습니다.

 

2. Particle.h

class Particle
{
public:
	Particle() : framesLeft_(0){}

	void Init(int lifeTime) { framesLeft_ = lifeTime; }
	// Animate 함수 수정 : 수명이 끝난 파티클 객체를 빈칸 리스트에 돌려주기위해 Life Time을 체크하도록 수정합니다.
	bool Animate()
	{
		--framesLeft_;

		return framesLeft_ == 0;
	}
	bool IsUsing() const { return framesLeft_ > 0; }

	// 추가된 코드
	// 1. 연결 리스트의 다음 Particle 객체를 가리키는 포인터 반환
	Particle* GetNext() const { return state_.Next_; }
	// 2. 연결 리스트의 다음 Particle 객체를 가리키는 포인터 할당
	void SetNext(Particle* Next) { state_.Next_ = Next; }

private:
	int framesLeft_;

	// 추가된 공용체 데이터 멤버
	union {
		// 1. 객체가 "사용 불가능" 상태일 때, 활용되는 구조체
		struct {
			//...
		} Live;

		// 2. 객체가 "사용 가능함" 상태일 때, 활용되는 파티클 객체 포인터
		Particle* Next_;
	}state_;
};

 

2. ParticlePool.h

class ParticlePool
{
public:
	ParticlePool()
	{
		// 1. 연결 리스트의 첫 번째 항목의 주소 값
		Head = &particles_[0];

		// 2. 다음 파티클 객체와 포인터를통해 연결
		for (int i = 0; i < POOL_SIZE; i++)
		{
			particles_[i].SetNext(&particles_[i + 1]);
		}

		// 3. 마지막 파티클 객체의 Next는 nullptr 입니다.
		particles_[POOL_SIZE - 1].SetNext(nullptr);
	}

public:
	// Create 함수 수정 : 새로운 파티클 객체를 빈칸 리스트에 삽입하는 작업으로 변경됩니다.
	void Create(int lifeTime)
	{
		// 연결 리스트가 비었는지 체크
		assert(Head != nullptr);

		// 연결 리스트의 삽입 작업
		Particle* newParticle = Head;

		Head = newParticle->GetNext();
		newParticle->Init(lifeTime);
	}
	// Animate 함수 수정 : 수명이 끝난 파티클 객체를 빈칸 리스트의 Head 앞으로 추가합니다. 
	void Animate()
	{
		for (int i = 0; i < POOL_SIZE; i++)
		{
			if (particles_[i].Animate() == true)
			{
				particles_[i].SetNext(Head);
				Head = &particles_[i];
			}
		}
	}

private:
	static const int POOL_SIZE = 100;
	Particle particles_[POOL_SIZE];

	// 추가된 연결 리스트의 Head
	Particle* Head;
};

 

고려 사항

1. 객체 < -> 풀 커플링

  • 커플링 되면, 더 간단하게 구현할 수 있습니다.
  • 커플링 되면, 객체가 풀을 통해서만 생성할 수 있도록 강제할 수 있습니다.
  • 커플링 되지 않으면, 다양한 객체를 풀에 담을 수 있습니다.
  • 커플링 되지 않으면, 객체의 "사용 가능함"상태를 외부에서 관리해야 합니다.

 

2. 재사용되는 객체의 초기화

  • 풀 안에서 초기화한다면, 풀은 객체를 완전히 캡슐화할 수 있습니다.
  • 풀 안에서 초기화한다면, 풀 클래스 자체는 객체가 초기화되는 방법과 결합됩니다.
  • 풀 밖에서 초기화한다면, 풀의 인터페이스는 간단해집니다.
  • 풀 밖에서 초기화된다면, 객체 생성의 성공 여부를 외부에서 관리해야 합니다.

 

 

 

 

 

 

 

 

'언어 > 디자인 패턴' 카테고리의 다른 글

[디자인 패턴]#16_공간 분할 패턴, Spatial Partition  (0) 2022.11.10
[디자인 패턴]#14_서비스 중개자 패턴, Service Locator  (0) 2022.10.09
[디자인 패턴]#13_이벤트 큐, Event Queue  (0) 2022.10.02
[디자인 패턴]#12_Component Pattern, 컴포넌트 패턴  (0) 2022.09.25
[디자인 패턴]#11_타입 객체, Type Object  (0) 2022.09.19
  1.  
  2. [디자인 패턴] #15_객체 풀, Object Pooling
'언어/디자인 패턴' 카테고리의 다른 글
  • [디자인 패턴]#16_공간 분할 패턴, Spatial Partition
  • [디자인 패턴]#14_서비스 중개자 패턴, Service Locator
  • [디자인 패턴]#13_이벤트 큐, Event Queue
  • [디자인 패턴]#12_Component Pattern, 컴포넌트 패턴
Hardii2
Hardii2
Hardii2
개발 블로그
Hardii2
전체
오늘
어제
  • 분류 전체보기
    • 알고리즘
    • 웹 개발
      • Node.js
      • React
    • 게임개발
      • DirectX12
      • 관련 지식
      • Unreal C++
      • Unreal 블루프린트
    • 언어
      • Effective C++
      • Basic C++
      • 디자인 패턴
      • 자료구조
      • 기술 질문
    • 문제 풀이
      • BOJ 문제 풀이
      • Programmers 문제 풀이
      • geeksForgeeks 문제 풀이
    • 수학
      • 확률과 통계
      • 게임수학
    • 개인프로젝트
    • 그룹프로젝트
      • PM
      • Dev
    • Github

블로그 메뉴

  • 홈
  • 글쓰기

공지사항

인기 글

태그

  • 정렬
  • 그래프
  • 디자인 패턴
  • 기술 질문
  • 우선순위 큐
  • Unreal Blueprint
  • set
  • BOJ
  • stl
  • unreal
  • DP
  • BFS
  • dfs
  • 최단 경로
  • 트리
  • C++
  • Effective C++
  • 알고리즘
  • programmers
  • 개발

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
Hardii2
[디자인 패턴]#15_객체 풀, Object Pooling
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.