양끝에서 할당하는 더블 헤디드 스택 할당기로 메모리 단편화를 막는 법
- 더블 헤디드 스택 할당기는 하나의 메모리 버퍼 양끝에서 각각 할당해서, 레벨 데이터와 캐릭터 데이터가 같은 메모리를 나눠 쓰면서도 한쪽이 다 차기 전까지 서로 간섭하지 않게 함
- 저자는 2010년 무렵 Surreal Software에서 스택 할당기를 애니메이션 블렌딩에 써서 할당을 정수 덧셈 하나로 줄였고, 프레임이 끝나면 tos를 0으로 되돌려 한꺼번에 해제함
- 스트리밍 월드 게임에서 레벨 지오메트리와 캐릭터 데이터가 제각각 할당과 해제를 반복하면 메모리 단편화가 생겨서, 빈 메모리가 40MB여도 20MB짜리 두 덩어리로 나뉘어 30MB 할당이 실패함
- 저자는 Blizzard StarCraft 2 팀의 James Anhalt에게 이 기법을 배웠고, 그가 과거에 겪은 단편화 문제의 해법으로 소개했음
Hacker News opinions
이거 멀티스레드에서 안전함? 버퍼에 1바이트 남았을 때 T1은 앞에서, T2는 뒤에서 동시에 잡으면 겹치지 않음?
동기화는 필요한데 보통 싸게 먹힘. 할당이 한 스레드에서 나와서 여러 소비자가 병렬로 쓰는 경우가 많아서 괜찮음.
두 tos를 64비트 하나에 묶고 CAS 돌리면 됨. 오프셋 두 개면 4GB까지 주소 되고 정렬 맞추면 더 늘어남. seqcst 신경 안 써도 돼서 그게 더 낫더라.
나는 GPU에 가중치 스트리밍할 때 매핑을 두 번 해서 가상주소로 wrap 시키는 방식 씀. 파이프라인 단계를 여러 개 앞서 잡을 수 있어서 이쪽이 더 셈. 양방향은 단계 1개밖에 못 뽑음.
이거 결국 옛날 유닉스 프로세스 모델이랑 똑같음. 힙은 bss 끝에서 위로 자라고 스택은 꼭대기에서 아래로 자라는 구조임.
카테고리가 두 개면 더블 헤디드가 반쪽짜리 스택 두 개보다 엄격히 나음. 같은 카테고리끼리 할당 해제 타임라인만 맞으면 단편화는 안 생김.
GC도 생각보다 나쁘지 않음. 게임 설계 바뀌면 수명도 쉽게 바뀌는데 한 프레임짜리 최적화에 목매는 건 손해임. 솔로 개발자는 수동 메모리 관리하다 일정 날아가더라.
나는 반대임. 시간당 크래시랑 프레임 드랍 중에 고르라면 프레임 드랍을 고르겠음.
아미가 OS에 MEMF_REVERSE 옵션 있었던 것도 같은 이유임. 95~96년쯤 1KB 넘는 할당을 전부 그걸로 돌리는 패치 만들었는데 단편화가 확 줄었음.