TECH 으로 돌아가기
TECH HACKER NEWS 오늘 7분 읽기 25 READS

C의 아레나 할당자: 메모리 관리를 단순화하는 오래된 기법

C 언어가 여전히 많은 개발자에게 부담스러운 이유 중 하나는 메모리를 직접 다뤄야 한다는 점이다. 필요할 때마다 malloc으로 작은 조각을 할당하고, 모자라면 realloc으로 늘리고, 다 쓰고 나면 free로 돌려줘야 한다. 문제는 이 free를 사람이 일일이 기억해야 한다는 데 있다. 특히 게임처럼 루프 안에서 반복적으로 할당이 일어나는 경우, free를 빠뜨리면 메모리가 계속 쌓이다가 결국 운영체제가 프로그램을 강제 종료시킨다. 작은 덩어리를 수없이 할당하고 해제하는 작업 자체도 느리다. 최근 공개된 한 기술 해설 글은 이런 고질적인 문제를 상당 부분 덜어주는 패턴으로 '아레나 할당자(arena allocator)'를 소개한다.

아레나 할당자란 무엇인가

핵심 아이디어는 단순하다. 메모리의 '수명(lifetime)', 즉 어떤 영역이 해제되기 전까지 얼마나 오래 쓰이는지를 기준으로 비슷한 것들을 묶는 것이다. 대부분의 수명은 몇 가지 범주로 분류할 수 있는데, 같은 시점에 함께 해제해도 되는 데이터라면 한꺼번에 큰 덩어리를 미리 확보해 두고 그 안에서 조금씩 꺼내 쓰면 된다. 메모리를 일부 사용할 때마다 '어디까지 썼는지'를 가리키는 포인터를 앞으로 전진시켜 사용 지점과 빈 공간의 경계를 기억한다. 그러다 그 안의 데이터가 더 이상 필요 없어지면 개별 조각을 하나씩 반납하는 대신 전체 덩어리를 한 번에 해제한다. 같은 메모리를 재사용하고 싶다면 아레나를 초기 상태로 되돌려 다시 처음부터 할당을 시작하면 된다.

이 방식은 느린 개별 할당을 없애준다는 성능상의 이점과 함께, 관리 대상을 작은 배열 단위가 아니라 몇 개의 '덩어리' 단위로 줄여준다는 실용적 이점을 함께 가진다. 신경 써야 할 대상이 줄어드니 자연히 메모리 누수 가능성도 크게 떨어진다.

구현의 뼈대

글쓴이가 제시하는 구현은 의외로 단출하다. 아레나 구조체는 세 가지 정보를 담는다. 사용된 메모리의 양을 추적하는 값, 할당이 아레나 용량을 넘기는지(오버플로)를 판단하는 데 필요한 전체 크기, 그리고 덩어리의 시작을 가리키는 data 포인터다. 여기에 새 아레나를 만드는 함수가 더해진다. 저자는 여러 값을 돌려줄 때 출력 매개변수(output parameter)를 쓰면 API가 직관적이지 않다고 보고, 대신 구조체를 통째로 반환하는 방식을 선호한다고 밝힌다.

할당에서 한 가지 눈여겨볼 지점은 정렬(alignment)이다. 컴퓨터는 8바이트 경계에 맞춰진 데이터를 훨씬 빠르게 처리하기 때문에, 새 할당의 시작점은 항상 8의 배수가 되어야 한다. malloc은 이 정렬을 알아서 처리해 주지만, 아레나는 이미 확보된 메모리에서 공간만 떼어 쓰는 방식이라 정렬을 직접 맞춰야 한다. 그래서 각 할당 끝에 다음 할당이 8바이트에 맞도록 패딩을 얼마나 넣을지 계산하는 과정이 들어간다. 오류 처리는 반환 구조체에 success 필드를 두고, = {0}으로 0 초기화된 상태에서 실패 시 그대로 조기 반환하는 패턴을 쓴다.

크기를 바꾸는 realloc 격 래퍼도 있지만 동작 원리는 다르다. 아레나는 개별 할당을 해제할 수 없으므로, 새 크기만큼 아레나 끝에 다시 할당하고 데이터를 복사한다. 옛 데이터는 건드리지 않고 그대로 두는데, 어차피 아레나 전체가 해제될 때 함께 사라지기 때문이다. 표준 realloc은 포인터마다 할당 크기를 내부적으로 알고 있어 새 크기만 넘기면 되지만, 이 아레나 구현은 할당 크기를 전혀 추적하지 않으므로 사용자가 기존 크기를 직접 넘겨줘야 한다. 크기 추적 기능을 넣을 수도 있지만 단순함을 위해 생략했다는 설명이다.

문자열 라이브러리와 실무적 함의

저자는 자신이 만든 문자열 라이브러리에 아레나를 적용한 사례를 든다. 이 라이브러리는 데이터를 들여다보기만 하는 '문자열 뷰(string view)'와 데이터를 소유하는 '소유 문자열(owned string)'을 구분한다. 소유 문자열은 NUL로 끝나며 직접 수정할 수 있는 반면, 여러 뷰가 같은 데이터를 바라볼 수 있는 뷰는 수정 시 다른 뷰에 영향을 주므로 직접 고칠 수 없다. 뷰를 소유 문자열로 변환하려면 새 메모리를 할당해 데이터를 복사하고 레거시 C API 호환을 위해 NUL 종결자를 붙여야 하는데, 이때 어느 아레나에 할당할지를 인자로 넘긴다.

여기에 아레나의 가장 큰 실무적 가치가 드러난다. 호출하는 쪽이 아레나를 넘김으로써 수명을 스스로 결정하게 되고, 따라서 개별 해제를 신경 쓸 필요가 없어진다. 메모리는 수명이 끝나 아레나가 폐기되거나 비워질 때 자동으로 사라진다. 그 시점은 프로그램 종료 시점일 수도 있고(이 경우 운영체제가 정리하므로 굳이 해제할 필요조차 없다), 함수 종료 시점일 수도 있으며, 더 이상 필요 없다고 확신하는 임의의 순간일 수도 있다. '이 함수가 내부적으로 할당을 하는가, 그렇다면 누가 언제 해제해야 하는가'라는 C 특유의 모호함이 아레나를 넘기는 관례 하나로 상당히 해소되는 셈이다.

다만 글쓴이 스스로 밝히듯 이 구현은 전문가의 완성품이 아니라 개인적 용도로 만든 입문용 예제다. 할당 크기를 추적하지 않아 리사이즈 시 기존 크기를 사용자가 떠안아야 하고, 개별 해제가 불가능하다는 구조적 제약도 있다. 수명이 뒤섞이거나 예측하기 어려운 데이터에는 오히려 맞지 않는다. 그럼에도 저자는 아레나를 습관적으로 쓰기 시작한 뒤 만족도가 높았다고 말한다. 개발자 경험의 개선 폭은 상황마다 다르지만, 이 도구를 손에 쥐고 있다는 것 자체가 순이득이라는 평가다. 수명이라는 관점에서 메모리를 바라보게 해준다는 점만으로도, C로 작업하는 개발자가 한 번쯤 익혀둘 만한 사고의 틀이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://eliasebner.com/blog/guides/arena-allocators-in-c/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...