C 아레나 할당자 구현 글, 매크로 우선순위와 타입 재정의 논쟁으로 번짐
- 아레나 할당자는 큰 메모리 덩어리를 한 번 잡아두고 포인터만 옮겨 쓰며, 수명이 같은 데이터를 묶어서 한꺼번에 해제함
- 개별 malloc과 free 호출이 줄어서 할당이 빨라지고, 해제를 빼먹어서 생기는 메모리 누수 위험도 크게 낮아짐
- 구현은 arena.c 한 파일이며, arena_create가 구조체를 값으로 반환하는 arena_create_out에 성공 여부 bool을 담아 호출자가 결과를 확인하게 함
- types.c에 u32 같은 타입 별칭과 KiB, MiB 같은 크기 매크로를 두고 unity build로 소스를 직접 포함함
Hacker News opinions
void 포인터에 산술 연산하는 건 표준에서 정의 안 된 동작이라 컴파일러가 봐주는 거뿐임. data는 char 포인터로 바꾸는 게 맞음. realloc도 new_size가 old_size 이하면 아무것도 안 하게 만드는 게 나음.
stdint 타입을 u32 같은 이름으로 재정의하는 건 라이브러리 공개 API로 새어나가지만 않으면 괜찮음. 옛날 MSVC가 C99를 늦게 따라와서 생긴 습관인 듯함.
8바이트 정렬은 괜찮은데 64비트에선 malloc이 보통 16바이트로 맞춰주니까 16으로 올리는 편이 나음. 정렬 안 맞은 포인터 캐스팅은 UB라 실제로 터질 수도 있음.
비정렬 접근은 요즘 주류 CPU에선 대체로 빠르고 잘 돎. 캐시라인을 넘기거나 SIMD 쓸 때만 문제임. 그래도 대상 플랫폼에서 테스트 안 한 코드는 지원 안 하는 거라고 봐야 함.
매크로에 괄호 안 쳐진 거 진짜 위험함. MIN(3,4)를 2 * 이렇게 곱해 쓰면 우선순위 때문에 결과가 꼬임. 이런 버그는 나중에 디버깅하기 진짜 힘듦.
int랑 i32 크기가 같다고 가정하는 건 너무 깐깐한 지적인 것 같음. 그래도 sizeof(int)는 sizeof(i32)로 바꾸는 게 맞긴 함.
malloc 결과 캐스팅은 C++ 모드로도 빌드하려고 넣는 거라 중복일 뿐 틀린 건 아님.
primitive 타입을 i32 같은 이름으로 typedef하면 다른 라이브러리 i32랑 충돌날 수 있어서 피하는 게 좋음.
나는 코드 하이라이팅 안 써도 되고 짧은 이름이 오히려 표현력에 도움 된다고 생각함.
구조체를 값으로 반환하고 [[nodiscard]] 붙이는 거 깔끔해서 보기 좋더라.
이거 아예 바이너리 스펙 하나 만들고 ISA별 어셈블리 구현을 붙이는 게 맞지 않나 싶음.