정적 할당과 no-op 주문으로 OOM과 use-after-free의 실패 범위를 제한하는 방법
- 정적 할당은 시작 시
--orders-max=1_000_000같은 상한만큼Order배열을 확보하고, 이후 요청이 상한을 넘으면 거절하는 방식임. 런타임에 주문 하나를 더 할당하다 OOM killer가 엔진이나 감독 프로세스를 종료하는 상황을 피하려는 설계임 - 객체 풀에서 해제된
T슬롯을 다시T에만 재사용하면 논리적 use-after-free는 남지만 서로 다른 타입의 메모리가 겹쳐 생기는 물리적 type confusion을 대부분 막음. 단, 객체 안의 inline enum은 다른 variant로 바뀌어 같은 문제가 다시 생김 - 할당 함수가 크기와 정렬이 지워진 인터페이스 대신
T타입 인자나 타입 증명을 받으면, 내부에서 타입별 분리 풀을 만들 수 있음.U의 해제 메모리를T에 재사용하지 않아 메모리 효율은 일부 낮아지지만 타입 혼동을 줄일 수 있음 - TigerStyle은 초기화 뒤 OS 수준의 동적 메모리 할당을 금지하고, 미리 확보한 배열을 풀처럼 사용하라고 제안함. 글은 비트셋이나 free-list로 빈 객체를 추적하는 풀 구조도 예시로 듦
- 글은 별도 할당 인덱스 대신 모든 주문 슬롯을 항상 파이프라인에 흘리고, 비활성 슬롯에는 no-op 주문 상태를 넣는 대안도 제시함. 상태 표식이 주문 안에 있어 별도 추적 자료구조와 불일치할 여지가 줄어듦
Hacker News opinions
임베디드에서는 정적 할당이 사실상 표준이고 잘 먹힘. 다만 동적 워크로드를 전제로 한 OS에서 새 하드 캡을 둔 설계 패턴을 익히는 부담이 기존보다 작을지는 모르겠음.
동적인 상황도 지역적으로는 정적 풀로 바꾸고, 풀이 다 차면 메모리를 더 확보한 뒤 continuation을 다시 진입시키는 식으로 쓸 수 있더라. 문제는 뻔하지만 중간 지점으로는 흥미로움.
예약된 값을 유효한 값으로 둬도 결국 실제 엔터티인지 예약값인지 검사해야 하는 것 아닌가? 뭐가 더 단순해지는지 아직 잘 모르겠음.
예약값은 switch/case에 다른 의사 타입과 함께 넣고 즉시 빠져나오게 하면 됨. Null 검사나 try/catch로 별도 제어 흐름을 만들 필요가 없음.
이 방식은 행복 경로만 생각하게 되는 문제를 줄임. 항상 뭔가를 처리하게 되니 런타임에서 놓친 경우가 숨은 제어 흐름으로 끼어들지 않음.
이런 엄격한 제약과 설계 규칙을 지키는 시스템에서 일하고 싶긴 함. 하지만 큰 조직에서는 각자가 전체 운영 모델을 보지 못한 하위 시스템을 만들고, 전역 제약은 결국 답답하게 느껴질 수 있음.
NULL order 배열을 만들어 호출자에게 빌려주면, 동적 할당 금지의 정신을 지킨 건지 의문임. 남의 검증된 할당기가 느리거나 버그가 날까 봐 업무 로직 안에 자체 할당기를 넣는 셈 아닌가?
이건 대체로 성능 얘기가 아니라 실패를 줄이는 얘기임. 시작 시 메모리를 잡아 두면 OOM에 견디게 됨.
TigerStyle이 금지하는 건 OS 관점의 동적 할당임. 같은 크기와 정렬을 가진 객체 풀은 실제 할당기보다 훨씬 단순하고, free-list만으로도 꽤 충분함.
인프라에서 속도와 신뢰성이 중요하면 용도별 메모리 배치와 특화 풀을 쓰는 게 맞음. Zig 표준 라이브러리에도 특화 할당기가 있어서 꼭 직접 버그 있는 구현을 쓸 필요는 없음.
글 후반의 방식은 할당 여부를 따로 추적하지 않고 모든 order를 같은 파이프라인에 흘리되 일부를 거의 no-op으로 만드는 것임. 비활성 order까지 순회하는 비용은 있지만, 최대 배열을 실제로 순회할 수 있음을 미리 검증하고 캐시 지역성도 좋아짐.