이번 로컬 LLM 프로젝트를 시작할 때 세웠던 가설 하나가 있다. “MoE(Mixture of Experts) 구조 모델은 토큰마다 일부 전문가(expert) 파라미터만 활성화하니까, 활성 파라미터 수가 작을수록 CPU에서도 더 빠르고 가벼울 것이다.” DeepSeek-Coder-V2(활성 2.4B)를 Qwen3-Coder-30B(활성 3.3B)보다 더 유리하게 볼 근거로 삼았던 가설이다.

실측해보니 속도 면에서는 어느 정도 맞아떨어졌지만(DeepSeek이 가장 빨랐다), 메모리 사용량 관점에서는 이 가설이 틀렸다.

계산에 쓰는 파라미터 ≠ 메모리에 올려야 하는 파라미터

MoE 모델이 “일부 파라미터만 쓴다"는 건 매 토큰의 계산량에 대한 이야기다. 그런데 어떤 토큰에서 어떤 expert가 선택될지는 그 순간의 입력에 따라 동적으로 정해진다. 즉 이번 토큰에서 안 쓰인 expert가 다음 토큰에서는 쓰일 수 있다 — 결국 모든 expert가 언제든 호출될 수 있으므로, 전체 파라미터가 메모리에 상주하고 있어야 한다.

Ollama(정확히는 그 기반인 llama.cpp)는 모델 파일을 통째로 메모리에 매핑(memory-map)해서 로드한다. 이건 MoE든 dense든 마찬가지다. 그래서 “모델 파일 크기 ≈ 실제 RAM 점유량 ≈ 메모리 대역폭 비용"이라는 등식이, 활성 파라미터 수와 무관하게 거의 그대로 성립한다.

실측으로 확인한 근거

Qwen3-Coder-30B(모델 파일 약 19GB) 추론 중 ps aux로 확인한 실제 메모리 사용량은 RSS 기준 약 19.3GB였다 — 활성 파라미터가 3.3B(전체의 약 11%)뿐인데도, 메모리는 모델 전체 크기만큼 그대로 잡아먹은 것이다.

simon  40646  522.2  33.1  46212824  19300000  ??  R  ... llama-server --model .../qwen3-coder-30b...

즉 MoE의 이점은 **계산 속도(초당 처리하는 연산량)**에서만 나타나고, 메모리 요구량에서는 전혀 이득이 없다. CPU 환경에서는 메모리 대역폭(데이터를 얼마나 빨리 읽어올 수 있는가)이 병목이 되는 경우가 많은데, 이 병목은 활성 파라미터 수가 아니라 전체 모델 크기에 의해 결정된다는 뜻이다.

이 사실이 갖는 의미

이 구조를 이해하고 나니, DeepSeek-Coder-V2-16B가 세 모델 중 가장 빨랐던 이유가 명확해졌다. 활성 파라미터가 작아서가 아니라(그건 부차적 요인), 모델 파일 자체가 가장 작았기(약 12~13GB) 때문에 메모리 대역폭 부담이 가장 적었던 것이다. 반대로 Qwen3-Coder-30B는 활성 파라미터는 더 컸어도, 결정적으로는 파일 크기(19GB)가 가장 컸기 때문에 로딩과 추론 모두에서 불리했다.

CPU 전용 환경에서 로컬 LLM을 고를 때는 “활성 파라미터 수"보다 양자화된 모델 파일의 실제 용량을 먼저 봐야 한다는 게, 이 프로젝트에서 얻은 가장 실용적인 교훈 중 하나다.