✅ 메모리 풀의 필요성

프로그램에서 new, delete는 편리하지만 느립니다. 특히 작은 객체를 수없이 반복 생성/소멸하는 경우 성능 병목의 원인이 됩니다. 이럴 때 등장하는 해결책이 바로 메모리 풀(Pool Allocator) 입니다.

✔️ 메모리 풀의 핵심 아이디어

  • 일정 크기의 메모리를 미리 한 번에 할당해 놓고
  • 실제 사용할 땐 그 안에서 빠르게 재사용한다

즉, malloc/free or new/delete의 호출 횟수를 대폭 줄이는 것이 핵심입니다.


🧱 기본 메모리 풀 구현 - 단일 스레드 버전

🔸 구조체 정의

class MemoryPool
{
public:
    MemoryPool(int32 allocSize);
    ~MemoryPool();

    void* Allocate();
    void  Release(void* ptr);

private:
    void Push(SListEntry* entry);
    SListEntry* Pop();

private:
    int32 _allocSize = 0;
    SListHeader _header = {};
};
  • allocSize: 할당받을 블록의 크기
  • _header: SLIST 구조를 사용하여 메모리 블록을 스택처럼 관리

🔸 생성자: 블록 크기 설정

MemoryPool::MemoryPool(int32 allocSize) : _allocSize(allocSize)
{
    assert(allocSize >= sizeof(SListEntry));
    ::InitializeSListHead(&_header);
}
  • SLIST는 Windows에서 제공하는 Lock-Free Stack입니다.
  • SLIST 사용 조건: 할당할 블록 크기 ≥ SLIST 노드 크기
  • 초기화 함수 InitializeSListHead를 통해 _header 준비 완료

🔸 메모리 할당 (Allocate)

void* MemoryPool::Allocate()
{
    SListEntry* entry = Pop();
    if (entry != nullptr)
        return static_cast<void*>(entry);

    return ::_aligned_malloc(_allocSize, SLIST_ALIGNMENT);
}
  • 우선 Pop()을 통해 재사용 가능한 블록이 있는지 확인
  • 없다면 새 메모리 블록을 aligned_malloc으로 생성
    • SLIST 호환을 위해 반드시 16바이트 정렬 필요

🔸 메모리 반환 (Release)

void MemoryPool::Release(void* ptr)
{
    Push(static_cast<SListEntry*>(ptr));
}
  • 할당된 메모리를 풀에 되돌리는 함수
  • Push 함수를 통해 재사용 리스트에 추가

🔸 Push & Pop 구현

void MemoryPool::Push(SListEntry* entry)
{
    ::InterlockedPushEntrySList(&_header, entry);
}

SListEntry* MemoryPool::Pop()
{
    return ::InterlockedPopEntrySList(&_header);
}
  • Interlocked 시리즈 함수는 SLIST에서 스레드 안전하게 Lock-Free Stack을 다루기 위한 함수
  • 멀티쓰레드 환경에서도 안전하게 작동

🔸 소멸자: 남은 메모리 정리

MemoryPool::~MemoryPool()
{
    while (true)
    {
        SListEntry* entry = Pop();
        if (entry == nullptr)
            break;

        ::_aligned_free(entry);
    }
}
  • 프로그램 종료 시 풀에 남아 있는 블록을 모두 해제하여 메모리 누수 방지

🧠 핵심

기능설명
Allocate()SLIST에서 꺼내거나 새로 할당
Release()SLIST에 메모리 반환
Push()/Pop()Interlocked 함수로 Lock-Free Stack 관리
_aligned_mallocSLIST 호환을 위한 16바이트 정렬 필요

🧪 성능 테스트 (예시)

MemoryPool pool(4096);

vector<void*> v;
for (int32 i = 0; i < 100'0000; i++)
{
    void* ptr = pool.Allocate();
    v.push_back(ptr);
}
::shuffle(v.begin(), v.end(), mt19937(random_device()()));
for (void* ptr : v)
{
    pool.Release(ptr);
}
  • 성능이 중요한 곳에서는 기본 new/delete 대신 메모리 풀을 활용하면 수배~수십 배 빠른 성능 확보 가능

profile
李家네_공부방

0개의 댓글