공부/그 외

메모리 풀(Memory Pool)

sudo 2021. 7. 14. 19:49

학부생때 들어보기만한 메모리풀을 직접 구현해봤다. 듣기만 해서 대충 개념만 알고 있어서 직접 구현하려니 어디서부터 시작해야할지 몰랐다. 그래서 구체적으로 메모리 풀이 어떤건지 개념을 다시 정리하는 것 부터 시작했다. 위키피디아에선 메모리 풀은 고정된 크기의 블록을 할당하여 malloc이나 C++의 new 연산자와 유사한 메모리 동적 할당을 가능하게 해준다. memory pool은 불리는 동일한 사이즈의 메모리 블록들을 미리 할당해 놓는다. 그러면 응용 프로그램들은 실행 시간에 핸들에 의해서 표현되는 블록들을 할당하고, 접근하고, 해제할 수 있다.

 

이 말을 조금 나의 방식대로 풀어서 설명하면, 거대한 덩어리의 메모리 블록을 미리 할당해놓고, 이것을 사용자가 원하는 만큼 조금씩 떼어서 준다는 것이다.

 

왜 사용해야 하는가?

malloc, new에는 자체적으로 메모리 단편화 방지 알고리즘이 포함되어 있지만 고가용성의 (고가용성(High Availability):  서버와 네트워크, 프로그램 등의 정보 시스템이 상당히 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다) 메모리 파편화(fragmentation)가 생길 수도 있다. 그리고 malloc이나 new도 내부에서 system call로 인한 overhead가 생길 수 있다. stackoverflow의 답변을 보자

https://stackoverflow.com/questions/6530355/is-memory-allocation-a-system-call

해석하면 malloc과 new 자체로는 system call이 아니지만, 메모리 할당을 하기 위해 low-level mechanism을 사용하고 윈도우에서는 VirtualAlloc() 함수를, POSIX에서는 mmap()이라는 system call을 사용한다고 한다. 그리고 연속된 메모리 할당이라면 system call없이 할당을 해줄 수도 있다고 한다. 결국 결론은 malloc, new로 인한 오버헤드가 있을 수 있다는 의미로 보인다.

 

 

코드 리뷰

CTest::RunPool() 함수에서 아래와 같이 PoolManager에게 메모리 할당을 요청한다. PoolManager는 CTest 클래스에 이렇게 선언되어 있다

std::unique_ptr<CPoolManager> PoolManager

unique_ptr은 이 객체가 가리키는 주소를 다른 포인터도 가르키고 있지 않게 하기위해서 + 이전에 공부해본거라 한번 써먹어 보고 싶기도 해서 써봤다.

void CTest::RunPool()
{
		for (int i = 0; i < TEST_LOOP_COUNT; ++i)
		{
			int* ptr1 = reinterpret_cast<int*>(PoolManager->Allocate(100));

			PoolManager->Deallocate(ptr1);

		}
}

RunPool함수에서 void* CPoolManager::Allocate(size_t size)로 점프하는데 만약 메모리 할당이 처음이라면 CMemoryBlock* 객체인 MemoryBlockHandle을 이용해서 void* CMemoryBlock::Allocate(size_t size)를 호출한다. CMemoryBlock::Allocate에서는 메모리를 size단위로 조각내서 linked list 방식으로 연결해준다. CMemoryBlock::Allocate의 리턴값은 CMemoryBlock의 생성자에서 malloc을 사용해서 arena_size만큼 할당한 거대한 메모리 덩어리(Allocate 해줄 때 마다 반환해줄 각 메모리 블록의 모음)의 시작 주소이다.

	int count = GetCount();

	auto ret = m_FreeBlock;

	for (int i = 0; i < count; ++i)
	{
		auto p = reinterpret_cast<char*>(m_FreeBlock) + size;
		if (i != count - 1)
		{
			m_FreeBlock->setNext(p);
		}
		else
		{
			m_FreeBlock->setNext(nullptr);
		}
		m_FreeBlock = m_FreeBlock->GetNext();
	}

 

만약 메모리 할당이 처음이 아니라면 CMemoeyBlock::Allocate를 호출하지 않고 CPoolManager::Allocate에서 단순히 다음 블록을 리턴해준다.

	auto ret = m_MemoryChunk;
	m_MemoryChunk = m_MemoryChunk->GetNext();


	return ret;

Performance 비교

malloc/free를 사용할 때 걸린 시간과 내가 사용한 Allocate/Deallocate를 사용할 때 걸린 시간을 비교하는 방식으로 performance를 비교해보았다. 당연히 단순히 malloc을 사용한 메모리 할당보단 좋을 것이라 생각했지만 생각보다 성능이 드라마틱하게 좋아지진 않았던 것 같다. 

Limitation

내가 참고한 서적이나 블로그의 코드들보다 성능이 떨어졌다. 어떻게 하면 개선시킬 수 있는지 고민해볼 것이다.

 

 

 

구현한 코드는 https://github.com/DkLee3/Pool-Project 여기에 올려두었다.

 

Reference

https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC_%ED%92%80

 

메모리 풀 - 위키백과, 우리 모두의 백과사전

메모리 풀(memory pool)은 고정된 크기의 블록을 할당하여 malloc이나 C++의 new 연산자와 유사한 메모리 동적 할당을 가능하게 해준다. malloc이나 new 연산자 같은 기능들은 다양한 블록사이즈 때문에

ko.wikipedia.org

https://snowfleur.tistory.com/171

 

[C++] 메모리 풀(Memory Pool)

메모리 풀(Memory Pool) 개요 메모리 풀(Memory Pool)은 고정 된 크기의 블록을 할당하여 malloc 이나 C++의 new 연산자와 유사한 메모리 동적 할당을 가능하게 해준다. malloc 이나 new 연산자 같은 기능들은

snowfleur.tistory.com

https://www.risebong.co.kr/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8B%A8%ED%8E%B8%ED%99%94/

 

메모리 단편화 - RiseBong!

오늘은 어플리케이션 메모리 단편화에 대해 이야기해보고자 합니다. 일단 일반적인 개발방법으로 개발을 진행했다면 메모리 단편화는 반드시 일어납니다.고가용성 프로그램 개발을 목적으로

www.risebong.co.kr

https://ko.wikipedia.org/wiki/%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1

 

고가용성 - 위키백과, 우리 모두의 백과사전

고가용성(高可用性, HA, High Availability)이란 서버와 네트워크, 프로그램 등의 정보 시스템이 상당히 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다. 고(高)가용성이란 "가용성이 높

ko.wikipedia.org