C++ Allocator의 기본 원리와 실무에서의 활용

//
//

들어가며

C++ STL 컨테이너를 보다 보면 이런 코드를 자주 보게 됩니다.

std::vector<int, std::allocator> numbers;

그런데 대부분의 경우, allocator는 직접 건드리지 않습니다.

왜냐하면, 기본 allocator만 사용해도 대부분 잘 동작하기 때문입니다.

그래서 처음 allocator를 접하면 이런 생각이 들기도 합니다.

“도대체 allocator는 왜 존재하는 걸까?”

혹은:

“실무에서는 allocator를 진짜 사용하는 걸까?”

이번 글에서는

  • allocator의 기본 개념
  • STL과 allocator의 관계
  • 메모리 할당 분리 구조
  • 실무에서 allocator가 중요한 이유
  • 게임 엔진에서 사용하는 allocator 구조

를 중심으로 정리해보겠습니다.


allocator란?

allocator는 쉽게 말하면 메모리 할당 정책을 담당하는 객체입니다.

즉,

메모리를 어디서,
어떤 방식으로,
어떻게 할당할 것인가?

를 담당합니다.

C++ STL 컨테이너들은 메모리 관리를 allocator에 위임합니다.

std::vector v;

내부적으로는 사실

std::vector<int, std::allocator> v;

와 비슷한 형태입니다.

즉, allocator는 STL 내부 메모리 관리 핵심 구조 중 하나입니다.


//

왜 굳이 allocator를 분리했을까?

핵심 이유는 “컨테이너와 메모리 정책을 분리하기 위해서”입니다.

예를 들어 vector 자체는 배열 관리 / 삽입/삭제 / capacity 관리에 집중합니다.

반면, 메모리를 어디서 가져올지는 allocator가 담당합니다.

즉,

컨테이너

데이터 구조 관리

allocator

메모리 관리

형태로 역할이 분리됩니다.

이 구조 덕분에 컨테이너는 메모리 정책과 독립적으로 동작할 수 있습니다.


기본 allocator는 결국 new/delete인가?

거의 그렇습니다.

기본 std::allocator는 내부적으로 결국 operator new / delete를 사용합니다.

예를 들어

T* ptr = allocator.allocate(n);

는 내부적으로 대략 이런 느낌입니다.

::operator new(sizeof(T) * n);

즉, 기본 allocator는 “STL 인터페이스 형태로 감싼 일반 힙 할당기”에 가깝습니다.


allocator가 중요한 진짜 이유

작은 프로젝트에서는 기본 allocator만 사용해도 충분합니다.

하지만 규모가 커지면 문제가 생깁니다.

대표적으로

  • 메모리 단편화(Fragmentation)
  • 할당 비용 증가
  • 멀티쓰레드 lock 비용
  • 캐시 효율 문제

같은 문제가 발생합니다.

특히 게임 엔진에서는 메모리 할당 자체가 성능 병목이 되는 경우가 많습니다.


왜 new/delete가 느려질까?

일반 힙 할당기는 굉장히 범용적으로 설계되어 있습니다.

  • 크기 다양한 메모리 처리
  • 멀티쓰레드 대응
  • OS 메모리 관리 연동

등을 모두 고려해야 합니다.

그래서 내부적으로

  • lock
  • free list 탐색
  • heap bookkeeping

같은 작업이 발생합니다.

즉, 반복적인 작은 메모리 할당에서는 비용이 커질 수 있습니다.


allocator의 핵심 아이디어

지금까지 설명한 allocator 구조와 메모리 관리 방식을 그림으로 정리해보면 다음과 같습니다.

핵심은 allocator는 단순 메모리 할당기가 아니라, 메모리 사용 패턴에 맞춰 성능 구조를 최적화하는 시스템이라는 점입니다.

C++ Allocator의 기본 원리와 Pool, Linear, Stack Allocator 구조를 설명하는 인포그래픽
C++ STL allocator 구조와 Pool Allocator, Linear Allocator, Stack Allocator 등 실무 메모리 관리 구조를 정리한 이미지입니다.

allocator의 핵심은 “사용 패턴에 맞는 메모리 관리”입니다.

예를 들어

  • 항상 같은 크기만 할당
  • 프레임 단위로 전체 해제
  • 한 번에 대량 할당

같은 패턴을 안다면 훨씬 빠른 allocator를 만들 수 있습니다.

즉, 범용 allocator보다 훨씬 효율적인 구조가 가능해집니다.


Pool Allocator

대표적인 allocator가 Pool Allocator입니다.

이 방식은 미리 큰 메모리 블록을 확보합니다.

그리고 같은 크기의 메모리를 빠르게 재사용합니다.

[64byte][64byte][64byte][64byte]

형태로 메모리를 관리합니다.

장점

  • 빠른 할당/해제
  • 단편화 감소
  • 캐시 효율 향상

특히, 게임 오브젝트 관리에서 자주 사용됩니다.


Linear Allocator

또 다른 대표 구조는 Linear Allocator입니다.

이건 굉장히 단순합니다.
현재 포인터 위치 증가만 수행합니다.

즉, 메모리 할당이 사실상 pointer 증가만으로 끝납니다.

굉장히 빠릅니다.

대신 개별 해제가 어렵습니다.

그래서

  • Frame Memory
  • Temporary Buffer

같은 곳에서 자주 사용됩니다.


Stack Allocator

Stack Allocator는 LIFO 구조를 사용합니다.

즉, 마지막에 할당한 메모리부터 해제가능합니다.

일반 stack 메모리와 비슷한 개념입니다.

장점

  • 매우 빠른 할당
  • 매우 빠른 해제

단점
사용 순서 제약이 큽니다.


STL allocator 인터페이스

allocator는 특정 인터페이스를 만족하면 STL에 연결 가능합니다.

대표적으로

allocate()
deallocate()

같은 함수들을 제공합니다.

template
class MyAllocator
{
public:

    using value_type = T;

    T* allocate(std::size_t n)
    {
        return static_cast<T*>(
            ::operator new(sizeof(T) * n));
    }

    void deallocate(T* ptr, std::size_t)
    {
        ::operator delete(ptr);
    }
};

그리고

std::vector<int, MyAllocator> v;

처럼 연결 가능합니다.


그런데 실무에서는 STL allocator를 많이 안 쓴다?

흥미로운 점은 실무 엔진 코드에서는 std::allocator 인터페이스를 직접 활용하지 않는 경우도 많다는 점입니다.

왜냐하면 엔진 레벨 메모리 시스템은 보통 STL보다 더 큰 범위를 관리하기 때문입니다.

  • Frame Allocator
  • Object Pool
  • Job System Memory
  • GPU Upload Buffer

즉, 엔진 전체 메모리 전략 자체를 설계하는 경우가 많습니다.


Unreal Engine도 allocator를 적극 사용한다

예를 들어 Unreal Engine도 독자적인 메모리 시스템을 사용합니다.

대표적으로:

  • FMalloc
  • Binned Allocator
  • TInlineAllocator

같은 구조가 존재합니다.

특히, TInlineAllocator는 흥미로운 구조입니다.

일정 개수까지는 힙 할당 없이 stack/local memory를 사용합니다.

즉, 작은 메모리 할당 비용을 줄이는 최적화입니다.


allocator의 진짜 목적

처음 allocator를 배우면 “메모리 할당기를 바꿀 수 있는 기능”

정도로 보이기도 합니다.

하지만 실제 핵심은 “메모리 사용 패턴을 구조적으로 제어하는 것”에 가깝습니다.

  • 어디에 저장할지
  • 언제 해제할지
  • 어떤 비용 구조를 가질지

를 설계하는 문제입니다.


현대 엔진에서 allocator가 중요한 이유

현대 엔진은

  • 멀티쓰레드
  • 대규모 데이터
  • 캐시 효율
  • GPU 업로드

문제를 모두 고려해야 합니다.

즉, 단순 new/delete만으로는 한계가 생기기 쉽습니다.

그래서 게임 엔진들은 점점
전용 allocator / 메모리 arena / frame memory / custom pool 구조를 적극적으로 사용하게 됩니다.


핵심 정리

  • allocator는 메모리 할당 정책을 담당하는 객체다
  • STL 컨테이너는 메모리 관리를 allocator에 위임한다
  • 기본 allocator는 사실상 new/delete 기반이다
  • 실무에서는 메모리 단편화와 lock 비용 문제가 중요해진다
  • Pool / Linear / Stack Allocator 같은 구조가 자주 사용된다
  • 현대 게임 엔진은 독자적인 메모리 시스템을 적극적으로 사용한다

마무리

allocator는 처음 보면, 다소 추상적이고 어렵게 느껴질 수 있습니다.

왜냐하면 작은 프로젝트에서는 기본 allocator만 사용해도 큰 문제가 없기 때문입니다.

하지만 프로젝트 규모가 커질수록 메모리 할당 자체가 성능 문제와 직접 연결되기 시작합니다.

그리고 이 시점부터 allocator는 단순 문법이 아니라, “엔진 성능 구조의 일부”가 됩니다.

개인적으로 allocator를 공부할 때 중요한 건 단순 인터페이스 암기보다,

  • 메모리 사용 패턴
  • 캐시 효율
  • 할당 비용 구조

를 함께 이해하는 것이라고 생각합니다.

👉 게임 엔진 구조를 더 깊이 이해하고 싶다면

아래 강의를 통해 직접 구현해보는 것을 추천드립니다.

C++로 만드는 게임 엔진 프레임워크 강의

👉 C++로 만드는 게임 엔진 프레임워크 강의 바로가기

//
   

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다