- Published on
26B 모델을 2GB 램으로 돌리는 원리 — 상주 메모리와 워킹셋은 같은 숫자가 아니다
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며 — "2GB"는 무엇을 세는 숫자인가
- 14.3GB는 어디서 나오나 — 저장 용량 산수부터
- MoE가 만드는 두 종류의 가중치
- mmap을 버리고 pread로 간 이유
- 토큰당 285MB — 대역폭이 상한을 정한다
- 상주 메모리와 워킹셋 — 2GB가 참인 지점과 오도하는 지점
- 4비트의 품질 비용, 그리고 이 방식이 맞는 자리
- 마치며 — 숫자가 세는 대상을 먼저 확인하면 대부분의 마법은 설명된다
들어가며 — "2GB"는 무엇을 세는 숫자인가
2026년 7월 29일 Show HN에 TurboFieldfare라는 프로젝트가 올라왔습니다. Swift와 Metal로 짠 추론 런타임인데, Gemma 4 26B-A4B를 M 시리즈 맥에서 약 2GB 램으로 돌린다는 주장이 붙어 있습니다. 저장소 README가 적은 수치는 M2 MacBook Air 8GB에서 초당 5.1~6.3 토큰, M5 Pro 24GB에서 31~35 토큰입니다.
"26B를 2GB에"라는 문장은 반사적으로 의심하게 됩니다. 그런데 이 프로젝트의 숫자들은 서로 잘 맞물립니다. 실제로 계산해 보면 대부분 정확합니다. 흥미로운 지점은 주장이 틀렸다는 데 있지 않고, 2GB가 무엇을 세는 숫자인지에 있습니다. 그건 프로세스 상주 메모리이고, 시스템이 실제로 쓰는 메모리와는 다른 값입니다. 그리고 이 차이가 M2 Air와 M5 Pro 사이 6배 성능 차이의 상당 부분을 설명합니다.
이 글은 README와 HN 스레드의 수치를 그대로 받아들이는 대신 직접 유도해서 맞춰 봅니다. 산수가 맞는 항목과, 맞지 않아서 다른 설명이 필요한 항목을 나눠서 보겠습니다.
14.3GB는 어디서 나오나 — 저장 용량 산수부터
먼저 디스크입니다. README는 텍스트 전용 모델 설치 용량을 약 14.3GB로, 양자화 방식을 "MLX affine 4비트, 그룹 64, 라우터는 8비트"로 적고 있습니다. Gemma 4 모델 카드 기준으로 26B-A4B는 총 25.2B 파라미터, 활성 3.8B, 컨텍스트 256K입니다.
4비트인데 왜 25.2B의 절반인 12.6GB가 아닐까요. 그룹 단위 아핀 양자화는 가중치만 저장하지 않기 때문입니다.
그룹 64 아핀 4비트의 실제 저장 비용
가중치 64개 x 4비트 = 32 바이트
스케일 1개 x fp16 = 2 바이트
영점 1개 x fp16 = 2 바이트
------------------------------------------
합계 = 36 바이트 / 64 가중치
가중치당 비트 수 = 36 x 8 / 64 = 4.5 bpw
모델 전체 = 25.2e9 x 4.5 / 8 = 14.18e9 바이트 = 약 14.2 GB
보고된 14.3GB와 1% 안쪽으로 맞습니다. 남는 0.1GB는 8비트 라우터, 임베딩 처리 방식의 차이, 메타데이터로 설명하면 충분합니다.
여기서 이미 하나의 교훈이 나옵니다 — "4비트 양자화"라는 표현은 파일 크기를 파라미터 수의 절반으로 만들어 주지 않습니다. 그룹 크기가 작을수록 품질은 좋아지고 오버헤드는 커집니다. 그룹 32였다면 bpw는 5.0이 되어 파일이 15.8GB가 됩니다. 로컬 실행 계획을 세울 때 이 0.5비트가 곧 1.5GB의 차이입니다. 가중치와 KV 캐시를 공식으로 계산하는 법은 로컬에서 LLM 돌리려면 VRAM이 얼마나 필요한가 편에 정리해 두었습니다.
MoE가 만드는 두 종류의 가중치
이제 램에 상주하는 1.35GB가 무엇인지 봅니다. README는 그것을 "공유 코어"라고 부릅니다. MoE 구조에서 파라미터는 두 부류로 갈립니다.
- 모든 토큰이 쓰는 것 — 임베딩, 어텐션, 공유 전문가, 라우터. 이건 상주시켜야 합니다.
- 토큰마다 골라 쓰는 것 — 라우티드 전문가. 레이어마다 128개 중 8개꼴로 활성화됩니다.
활성 파라미터가 3.8B이고 총 25.2B라는 두 숫자에서 이 분할을 역산할 수 있습니다.
S = 항상 쓰는 파라미터, R = 라우티드 전문가 파라미터
활성 비율 f = 활성 전문가 수 / 전체 전문가 수
S + R = 25.2B
S + f x R = 3.8B (f = 8/128 = 0.0625 가정)
0.9375 R = 21.4B -> R = 22.8B, S = 2.4B
S를 4.5 bpw로 저장하면
2.4e9 x 4.5 / 8 = 1.35e9 바이트 = 1.35 GB
README가 밝힌 공유 코어 1.35GB와 정확히 일치합니다. 활성 전문가 8 대 128이라는 구조 가정이 맞았다는 뜻이고, 동시에 이 설계의 핵심을 보여 줍니다. 전체의 5%만 상주시키면 나머지 95%는 토큰마다 필요한 조각만 가져오면 됩니다.
여기서 강조할 점은 이 방식이 Gemma 4가 MoE이기 때문에 성립한다는 것입니다. 같은 크기의 밀집(dense) 모델이었다면 토큰마다 25.2B 전부를 읽어야 하고, 그건 어떤 SSD로도 감당이 안 됩니다. 스트리밍 추론은 양자화 기법이 아니라 아키텍처가 열어 준 문입니다.
mmap을 버리고 pread로 간 이유
가중치를 지연 로딩할 때 가장 먼저 떠오르는 방법은 mmap입니다. 파일을 주소 공간에 매핑해 두고 접근하면 커널이 알아서 페이지를 가져옵니다. 코드가 단순하고, llama.cpp를 포함한 대부분의 로컬 추론 엔진이 이 방식을 씁니다.
그런데 HN 스레드에서 저자가 밝힌 바로는 mmap 구현이 초당 0.5토큰에 머물렀고, 병렬 pread 호출로 바꾸면서 약 4토큰으로 올라갔습니다. 여덟 배입니다. 이유는 두 방식의 동시성 모델이 다르기 때문입니다.
mmap으로 접근한 페이지가 아직 램에 없으면 페이지 폴트가 나고, 그 스레드는 폴트가 해결될 때까지 멈춥니다. 커널의 선행 읽기(readahead)는 순차 접근을 가정하는데, 전문가 라우팅이 만들어 내는 접근 패턴은 파일 전체에 흩어진 무작위 읽기입니다. 예측이 틀리니 매번 한 번에 하나씩 기다리게 됩니다. 반면 pread는 명시적인 요청이라 필요한 오프셋 목록을 한꺼번에 던져 놓고 SSD의 큐 깊이를 채울 수 있습니다. NVMe는 요청 하나를 빨리 처리하는 장치가 아니라 수십 개를 동시에 처리해 대역폭을 채우는 장치라서, 이 차이가 그대로 여덟 배로 나타납니다.
실제 구현은 여기에 두 가지를 더 얹었습니다. 레이어마다 16칸짜리 LFU 캐시를 두어 자주 쓰이는 전문가를 붙잡아 두고, 프리필 단계에서는 최대 128토큰씩 묶어 처리해 한 번 가져온 전문가가 여러 토큰을 처리하게 만듭니다. 그리고 CPU가 다음 전문가를 읽어 오는 동안 Metal은 공유 전문가 분기를 계산합니다 — 입출력과 연산을 겹치는 고전적인 기법입니다.
토큰당 285MB — 대역폭이 상한을 정한다
이제 성능의 상한을 계산할 수 있습니다.
라우티드 전문가 전체 = 22.8e9 x 4.5 / 8 = 12.8 GB
토큰당 활성 비율 = 8 / 128 = 6.25%
캐시가 전혀 없을 때 토큰당 읽어야 할 양
12.8 GB x 0.0625 = 800 MB
HN에서 저자가 보고한 실측: 토큰당 250~320 MB
역산한 캐시 적중률 = 1 - (250~320) / 800 = 60% ~ 69%
저자가 따로 보고한 값은 M2에서 59~69%, 16칸 캐시로 약 67%였습니다. 제가 유도한 60~69%와 맞아떨어집니다. 즉 README와 HN에 흩어진 숫자들은 하나의 일관된 모델에서 나옵니다. 토큰당 실질 전송량을 285MB 정도로 잡고 기기별 상한을 계산하면 이렇게 됩니다.
| 기기 | 램 | 인용된 SSD 순차 읽기 | 토큰당 285MB 기준 이론 상한 (유도) | 보고된 토큰당 디스크 시간 | 보고된 처리량 |
|---|---|---|---|---|---|
| M2 Air | 8 GB | 확인하지 못함 | — | 83 ms | 5.1~6.3 tok/s |
| M4 (대역폭 참고용) | — | 2,031 MB/s | 약 7 tok/s | — | — |
| M5 Pro | 24 GB | 6,323 MB/s | 약 22 tok/s | 12 ms | 31~35 tok/s |
M2 Air 쪽은 계산이 잘 맞습니다. 토큰당 83밀리초의 디스크 시간이면 초당 12토큰이 상한이고, 나머지 연산 시간을 더하면 실측 5~6토큰이 자연스럽습니다.
문제는 M5 Pro 줄입니다. 인용된 SSD 대역폭으로 계산한 상한이 약 22토큰인데 실측이 31~35토큰입니다. 토큰당 12밀리초에 285MB를 읽으려면 초당 23.75GB가 필요한데, 이건 어떤 소비자용 NVMe도 내지 못하는 속도입니다. 산수가 안 맞는 것이 아니라, 그 읽기의 상당 부분이 SSD에서 오지 않는다는 뜻입니다.
상주 메모리와 워킹셋 — 2GB가 참인 지점과 오도하는 지점
답은 macOS의 통합 버퍼 캐시입니다. pread로 읽은 파일 페이지는 커널이 램에 보관하고, 같은 오프셋을 다시 읽으면 SSD를 건드리지 않습니다. 24GB 머신에서 14.3GB짜리 모델 파일은 상당 부분이 캐시에 남을 수 있습니다. 워밍업이 끝난 뒤의 "디스크 읽기"는 실제로는 램 복사에 가까워집니다.
그런데 이 페이지들은 프로세스의 상주 메모리로 집계되지 않습니다. 파일 기반 페이지는 커널의 페이지 캐시에 속하고, 메모리 압박이 오면 커널이 회수합니다. 그래서 Activity Monitor에서 프로세스는 여전히 2GB로 보입니다. 주장은 거짓이 아닙니다 — 다만 세는 대상이 다릅니다.
정리하면 이렇습니다.
- 참인 부분: 이 런타임이 자기 힙에 붙잡아 두는 가중치는 공유 코어 1.35GB에 4K KV 캐시를 더한 정도이고, 프로세스 RSS는 2GB 근처입니다. 램이 8GB뿐인 기기에서 26B급 모델이 아예 돌아간다는 사실 자체가 이 설계의 성과입니다.
- 오도하는 부분: 시스템 전체가 쓰는 메모리는 2GB가 아닙니다. 성능을 내려면 커널 페이지 캐시가 모델 파일의 큰 조각을 들고 있어야 하고, 그 메모리는 다른 앱과 경쟁합니다. 브라우저 탭을 스무 개 열어 캐시가 밀려나면 처리량은 SSD 대역폭 상한으로 떨어집니다. M2 Air의 5~6토큰과 M5 Pro의 31~35토큰 사이 격차는 칩 성능만의 차이가 아니라 캐시로 쓸 수 있는 여유 램의 차이이기도 합니다.
- 따라서 의심할 부분: HN에서 나온 "이득이 기법이 아니라 OS 캐싱에서 오는 것 아니냐"는 지적은 절반 맞습니다. 다만
mmap에서pread로 바꿔 여덟 배가 난 것은 캐시로 설명되지 않으니, 기법의 기여도 실재합니다. 두 요인이 함께 있습니다.
그리고 산수가 닫히지 않는 항목이 하나 더 있습니다. 레이어당 16칸 캐시는 전체 128개 중 12.5%이므로, 전 레이어에 걸쳐 온전히 상주한다면 12.8GB의 12.5%인 약 1.6GB가 됩니다. 공유 코어 1.35GB와 합치면 2GB를 넘습니다. 실제 구현이 슬롯을 다르게 세거나, 그 버퍼의 상당 부분이 파일 기반이라 RSS에 잡히지 않거나 둘 중 하나일 텐데, 저는 어느 쪽인지 소스를 읽어 확인하지 못했습니다. 어느 쪽이든 결론은 같은 방향입니다 — 2GB는 프로세스 회계장부의 숫자입니다.
4비트의 품질 비용, 그리고 이 방식이 맞는 자리
마지막으로 공짜가 아닌 부분입니다. 4비트 양자화의 품질 손실에 대해 널리 인용되는 정리는, FP16 대비 퍼플렉시티가 1~3% 나빠지고 총 파라미터가 클수록 손실이 작다는 것입니다. MoE는 총 파라미터가 크므로 활성이 작아도 4비트를 비교적 잘 견딘다는 주장도 있습니다. 다만 이것들은 집계 글에서 반복되는 일반론이고, 이 특정 빌드에 대한 측정은 아닙니다.
정확히 말하면 이렇습니다. Gemma 4 26B-A4B의 공개 벤치마크는 양자화되지 않은 모델의 것입니다. 모델 카드가 싣는 MMLU Pro 82.6, GPQA Diamond 82.3, LiveCodeBench v6 77.1 같은 숫자는 4.5 bpw로 누른 이 빌드의 점수가 아닙니다. 그리고 이 빌드의 점수를 잰 사람은 아직 없습니다. README도 "모델이 반복하거나 틀린 답을 낼 수 있으니 중요한 결과는 확인하라"는 문장을 달아 놓았을 뿐입니다. 양자화된 로컬 모델의 품질을 이야기할 때 원본 모델 카드 숫자를 인용하는 것은, 이 글이 하지 말자고 주장하는 종류의 일입니다.
그러면 이 방식은 어디에 맞을까요.
- 맞는 자리: 램이 모자란 기기에서 오프라인으로, 지연에 민감하지 않은 작업. 문서 요약, 로컬 코드 설명, 네트워크가 없는 환경. 초당 5토큰은 대화형으로는 답답하지만 백그라운드 배치로는 충분합니다. 데이터를 밖으로 보내면 안 되는 상황이라면 대안이 별로 없습니다.
- 안 맞는 자리: 램이 충분한 기기. 24GB 이상이면 14.3GB짜리 모델을 그냥 통째로 올리는 편이 단순하고 빠릅니다. 이 방식의 존재 이유는 램 부족이지 성능이 아닙니다. 그리고 상시 가동하는 서버는 더더욱 아닙니다 — 이 런타임은 인스턴스당 단일 프로세스만 모델을 소유하고 동시 실행을 지원하지 않는다고 명시돼 있습니다.
- 확인되지 않은 것: SSD 수명에 대한 정량 자료가 없습니다. 읽기 중심이라 쓰기 마모는 크지 않을 것으로 보이지만, 저장소도 이를 수치화하지 않았다고 밝힙니다. 또 macOS 26과 Metal 4가 요구 사항이고, macOS 15에서 쓰려면 2.4배 프리필 가속을 포기하는 우회가 필요합니다. M1부터 M5까지 전 세대에서 같은 결과가 나온다는 근거도 아직 없습니다.
마치며 — 숫자가 세는 대상을 먼저 확인하면 대부분의 마법은 설명된다
"26B를 2GB에"는 과장이 아니었습니다. 4비트 그룹 64가 만드는 4.5 bpw, 총 25.2B와 활성 3.8B에서 역산되는 공유 코어 1.35GB, 라우티드 전문가 12.8GB의 6.25%인 토큰당 800MB, 캐시로 285MB까지 줄어드는 실전송량 — 이 네 숫자가 한 줄로 이어지고 보고된 값들과 맞습니다. 좋은 엔지니어링입니다.
동시에 2GB는 프로세스 상주 메모리이지 시스템 워킹셋이 아닙니다. M5 Pro의 실측 처리량이 SSD 대역폭 상한을 넘는다는 사실이 그 증거이고, 그 차이를 메우는 것은 커널 페이지 캐시입니다. 그러니 이 프로젝트를 8GB 맥에 걸어 볼 때 기대해야 할 숫자는 31~35토큰이 아니라 5~6토큰입니다.
로컬 추론의 성능 주장을 읽을 때 던져야 할 질문은 늘 같습니다. 그 숫자는 무엇을 세고 있으며, 세지 않은 것은 어디에 있습니까.