flash-moe로 Apple Silicon에서 MoE 모델 추론하기
flash-moe의 순수 C·Metal 설계, Apple Silicon 통합 메모리, MoE 모델 추론 성능과 제약을 정리한다.
2026-08-15 · 최초 발행 2026-03-24
3970억(397B) 파라미터 규모의 Mixture-of-Experts 모델을 개인용 M3 Max MacBook Pro에서 실행하려면, 모델 크기만큼이나 메모리를 어떻게 쓰고 연산을 얼마나 건너뛸 수 있는지가 중요하다. flash-moe는 이 지점에 집중한 순수 C 기반 추론 엔진이다. Python 생태계의 편의 계층을 걷어내고 Metal을 직접 사용해 Apple GPU의 동작 방식에 맞춘다.
C99와 Metal로 구성한 MoE 추론 경로
flash-moe는 PyTorch나 TensorFlow 같은 고수준 ML 프레임워크 없이 C99와 Metal Shading Language로 구현됐다. 목표는 소비자용 Apple Silicon에서 가능한 큰 MoE 모델을 실용적인 속도로 실행하고, Python 의존성 없이 배포하며, Metal을 통해 GPU 기능을 직접 활용하는 데 있다.
- 구현 언어: 순수 C99 + Metal Shading Language
- Python 의존성: 없음 (단일 바이너리 배포)
- 지원 하드웨어: Apple M1/M2/M3 계열 (Max/Ultra 권장)
- 지원 모델: Mixtral 8x7B/8x22B, DeepSeek-V2/V3, Qwen MoE 계열
- 최대 실행 모델: 397B 파라미터 (M3 Max 128GB RAM 기준)
- 양자화 지원: INT2, INT3, INT4, INT8
- 라이선스: MIT
토큰마다 일부 전문가만 계산하는 MoE
MoE는 Transformer 블록의 Feed-Forward Network(FFN)를 여러 전문가(Expert) 서브네트워크로 나눈 뒤, 라우터가 토큰마다 그중 일부만 선택하는 구조다. 전체 파라미터를 항상 계산하는 Dense 모델과의 차이는 이 선택 과정에서 생긴다.
397B 파라미터 MoE 모델도 토큰 하나를 처리할 때 실제 계산에 참여하는 파라미터는 전체의 약 10-20%다. 실질 연산량은 40-80B Dense 모델과 비슷해진다.
| 모델 유형 | 파라미터 수 | 실제 활성 파라미터 | 메모리 요구 | 연산 부하 |
|---|---|---|---|---|
| Dense 70B | 70B | 70B (100%) | ~35GB (INT4) | 높음 |
| MoE 141B | 141B | ~14-28B (10-20%) | ~70GB (INT4) | 중간 |
| MoE 397B | 397B | ~40-80B (10-20%) | ~100GB (INT3) | 중간 |
이 희소 활성화 덕분에 MoE는 전체 파라미터 규모에 비해 추론 속도가 빠르고 전력 소모도 낮다. 로컬 장비에서 대형 모델을 다룰 때 Dense 모델보다 유리한 이유다.
M3 Max 통합 메모리가 만드는 실행 가능 범위
LLM 추론은 풍부한 VRAM을 갖춘 NVIDIA GPU 중심으로 이뤄져 왔다. M3 Max는 CPU, GPU, Neural Engine이 같은 물리 메모리를 직접 사용하는 통합 메모리 아키텍처를 통해 다른 선택지를 제공한다.
NVIDIA GPU에서는 시스템 RAM과 GPU 메모리 사이의 전송이 PCIe 버스를 지나며 병목이 될 수 있다. 반면 M3 Max에서는 128GB까지 확장되는 통합 메모리를 GPU 연산에도 그대로 활용한다.
| 사양 | NVIDIA RTX 4090 | Apple M3 Max (128GB) | Apple M3 Ultra (192GB) |
|---|---|---|---|
| GPU 메모리 | 24GB VRAM | 128GB (통합) | 192GB (통합) |
| 메모리 대역폭 | 1,008 GB/s | 400 GB/s | 800 GB/s |
| TDP | 450W | 150W (시스템 전체) | 180W (시스템 전체) |
| 최대 모델 (INT4) | ~12B | ~60B | ~90B |
| 최대 모델 (INT2) | ~24B | ~120B | ~180B |
| 최대 MoE (INT3) | ~30B 활성 | ~397B | ~600B+ |
메모리 대역폭은 NVIDIA RTX 4090보다 낮지만, 사용할 수 있는 메모리 용량은 5배 이상 차이 난다. NVIDIA RTX 4090의 가격은 ~200만 원인 반면 M3 Max 구성은 MacBook Pro를 포함한 시스템 비용으로 판단해야 한다. 따라서 M3 Max의 선택은 최고 생성 속도를 얻는 방법이라기보다, 별도 GPU와 시스템 메모리 사이의 이동 없이 큰 모델을 한 기기에서 메모리에 올리는 선택이다.
Python 계층을 두지 않는 이유
flash-moe는 Python을 쓰지 않는다. Python 기반 추론 엔진에서 발생할 수 있는 GIL(Global Interpreter Lock), 동적 타입 해석, 가비지 컬렉션, JIT 워밍업의 오버헤드를 제거하기 위한 선택이다.
- GIL(Global Interpreter Lock): Python의 멀티스레딩을 병렬 연산이 아닌 협력적 멀티태스킹으로 제한
- 동적 타입 해석: 런타임에 타입을 결정하는 오버헤드
- 가비지 컬렉션: 대용량 텐서 할당과 해제 시 GC 일시 정지
- JIT 워밍업: PyTorch의
torch.compile()등 JIT 최적화가 첫 실행 시 지연 유발
컴파일된 단일 바이너리는 Python 환경 설정 없이 실행할 수 있다. 또한 Metal GPU의 접근 패턴에 맞춰 메모리 레이아웃을 제어할 수 있다.
Metal 커널에서 처리하는 메모리 병목
Metal을 직접 쓰는 설계는 MoE의 활성화 패턴과 메모리 접근을 맞물리게 한다.
Threadgroup 메모리 최적화에서는 Metal의 threadgroup 공유 메모리(~32KB per threadgroup)에 자주 쓰는 가중치 데이터를 캐시처럼 둔다. 전역 메모리 접근 횟수를 줄이는 방식이다.
비동기 메모리 전송은 CPU가 다음 레이어의 가중치를 GPU로 보내는 작업과 현재 레이어 연산을 겹친다. 메모리 대역폭 대기를 감추기 위한 처리다.
MoE 전용 커널은 Sparse Activation에 맞춘 Metal 셰이더를 통해 비활성 전문가 데이터에 대한 불필요한 메모리 접근을 없앤다.
양자화·어텐션·캐시와 프리페칭
**그룹 양자화(Group Quantization)**는 레이어 전체에 하나의 스케일 인자를 적용하는 대신, 128개 혹은 64개 파라미터 그룹별로 스케일과 제로포인트를 유지한다. 양자화 정밀도를 약간 희생하지만, 같은 비트폭에서 실제 수치 정확도는 높다.
Flash Attention Metal 구현은 표준 Attention 계산의 메모리 복잡도를 O(n²)에서 O(n)으로 줄이는 Flash Attention 알고리즘을 Metal 셰이더로 직접 구현한다. A19 Pro의 AMX(Advanced Matrix Extension) 유닛을 최대한 활용한다.
KV Cache 타일링은 키-밸류 캐시를 작은 타일로 나눠 Metal threadgroup 메모리에 올린 뒤 처리한다. 긴 컨텍스트에서 GPU 캐시 계층에 맞는 메모리 접근 패턴을 만든다.
전문가 프리페칭은 MoE 라우터가 라우팅 결정을 내릴 때 다음 레이어에서 사용할 전문가 가중치를 비동기로 미리 로딩한다.
M3 Max에서 측정한 모델별 속도
다음 값은 M3 Max MacBook Pro (128GB, 40코어 GPU) 기준 측정값이다.
| 모델 | 파라미터 | 양자화 | 활성 파라미터 | 토큰/초 (생성) | 메모리 사용 |
|---|---|---|---|---|---|
| Mixtral 8x7B | 46.7B | INT4 | ~12B | 18-22 | 24GB |
| Mixtral 8x22B | 141B | INT4 | ~39B | 6-8 | 70GB |
| DeepSeek-V2 | 236B MoE | INT3 | ~21B | 4-6 | 88GB |
| DeepSeek-V3 | 671B MoE | INT2 | ~37B | 2-4 | 105GB |
| 397B MoE (실험) | 397B | INT2 | ~40-80B | 1-2 | 100GB |
397B 모델의 초당 1-2 토큰은 빠른 대화에는 맞지 않는다. 장문 문서 분석이나 코드 생성처럼 응답 시간에 여유가 있는 작업에서는 사용할 수 있다. Mixtral 8x7B는 18-22 tok/s로 측정됐다.
llama.cpp와 다른 선택 범위
llama.cpp가 여러 모델과 플랫폼을 폭넓게 다루는 도구라면, flash-moe는 Apple Silicon에서 MoE 추론에 집중한다.
| 항목 | llama.cpp | flash-moe |
|---|---|---|
| 지원 모델 | Dense 중심, MoE 부분 지원 | MoE 전문 특화 |
| 구현 언어 | C/C++ | 순수 C99 |
| GPU 백엔드 | Metal, CUDA, Vulkan, etc. | Metal 전용 |
| 플랫폼 지원 | 크로스플랫폼 | Apple Silicon 전용 |
| MoE 최적화 | 범용 수준 | 전문 최적화 |
| 최대 모델 규모 | ~70B (일반 구성) | 397B+ |
| 커뮤니티 크기 | 매우 큼 | 소규모 특화 |
Apple Silicon과 MoE 모델의 조합에서는 flash-moe가 성능을 겨냥한 전문 도구가 된다. 크로스플랫폼 호환성이나 Dense 모델 지원이 필요하면 llama.cpp가 더 나은 선택이다.
빌드 후 모델 실행
flash-moe는 Python 환경을 구성하지 않고 CMake 빌드로 설치한다.
# 저장소 클론
git clone https://github.com/flash-moe/flash-moe
cd flash-moe
# CMake 빌드 (Xcode Command Line Tools 필요)
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(sysctl -n hw.logicalcpu)
# 모델 다운로드 후 실행 (예: Mixtral 8x7B INT4)
./flash_moe \
--model /path/to/mixtral-8x7b-int4.bin \
--prompt "안녕하세요, flash-moe를 테스트합니다" \
--n-predict 200
모델 파일은 GGUF 형식과 호환되며, llama.cpp로 변환된 MoE 모델을 그대로 사용할 수 있다.
로컬 추론의 전문화가 보여주는 것
flash-moe는 PyTorch 중심의 ML 환경 밖에서도 특정 하드웨어를 위한 순수 C 구현이 높은 성능을 낼 수 있음을 보여준다. 범용 프레임워크의 최적화와 하드웨어 특화 구현 사이에는 여전히 성능 차이가 존재한다.
동시에 이 프로젝트는 소수 개발자가 대형 기업의 인프라 없이도 전문적인 추론 엔진을 만들 수 있는 사례다. 의료 기록, 법률 문서, 기업 기밀 데이터를 클라우드 AI API에 전송하지 않고 로컬에서 처리할 수 있는 선택지가 생기며, 클라우드 API 비용이 부담스러운 대량 처리 작업을 로컬 하드웨어로 전환하는 가능성도 열린다. 인터넷 연결이 어려운 비행 중, 보안 환경, 개발도상국에서도 고성능 AI 기능을 활용할 수 있고, 고가의 GPU 클러스터 없이 대형 MoE 모델을 로컬에서 실험하는 연구 접근성도 넓어진다.
로컬 AI에서 통합 메모리와 MoE 희소성이 만나는 구간은, 개인 장비로 대형 모델을 운용할 수 있는 범위를 넓힌다.
선택 전에 확인할 제약
M3 Max 128GB 구성의 MacBook Pro에는 최소 400만 원 이상의 비용이 요구된다. MacBook Pro 최상위 모델 기준 500만 원 이상의 비용이 들며, 일반 M3 MacBook(8-24GB)에서는 Mixtral 8x7B 수준까지만 실용적으로 실행 가능하다.
이 비용은 모델을 메모리에 올릴 수 있는 범위를 넓혀 주지만, 생성 속도를 보장하지는 않는다. 397B 모델의 1-2 tok/s는 대화형 응용에 적합하지 않아 장문 문서 분석, 코드 생성, 배치 처리나 오프라인 분석처럼 응답 시간을 허용할 수 있는 작업에 맞는다.
flash-moe는 CUDA, ROCm, Vulkan 같은 다른 GPU 백엔드를 지원하지 않아 Linux와 Windows에서는 사용할 수 없다. 순수 추론 엔진이므로 로컬 파인튜닝이나 학습 기능도 제공하지 않는다. MoE에 특화된 만큼 Dense 모델 추론에서는 llama.cpp 대비 이점이 없으며, 소규모 프로젝트라 llama.cpp 수준의 활발한 커뮤니티 지원을 기대하기도 어렵다.