ik_llama.cpp를 포크하여 멀티 소켓 CPU 시스템의 성능을 극대화하는 '--numa mirror' 모드를 추가했습니다. 공유 및
요약
멀티 소켓 CPU 시스템에서 LLM 추론 성능을 극대화하기 위해 ik_llama.cpp에 '--numa mirror' 모드를 추가했습니다. 이 모드는 모델 가중치와 KV 캐시를 각 소켓에 복제하여 원격 메모리 접근으로 인한 성능 저하를 방지합니다.
핵심 포인트
- 멀티 소켓 환경의 NUMA 메모리 접근 병목 현상 해결
- 모델 가중치 및 KV 캐시 복제를 통한 노드 로컬 데이터 활용
- 메모리 사용량은 증가하지만 모든 CPU 코어를 효율적으로 사용 가능
- llama.cpp의 --numa isolate 방식 대비 멀티 CPU 활용도 향상
GitHub: https://github.com/mikechambers84/ik_llama.cpp/tree/numa-mirror
반드시 numa-mirror 브랜치를 확인해 주세요.
멀티 소켓 (multi-socket) CPU 시스템을 추론 (inference)에 사용하려는 분들을 위해 이 내용을 공유합니다. 저는 오랫동안 NUMA 미러 (NUMA mirror) 모드를 원해왔고, 마침내 ik_llama.cpp를 포크하여 이를 추가했습니다.
ik_llama.cpp는 CPU 추론을 위한 주요 성능 향상을 추가한 llama.cpp의 포크 (fork) 버전이므로, 기본 llama.cpp보다는 이곳을 포크하는 것이 타당했습니다.
이 기능이 해결하고자 하는 문제를 모르는 분들을 위해 설명하자면, 멀티 소켓 머신은 각 소켓에 로컬 (local)인 메모리를 가지고 있습니다. CPU가 자신의 로컬 메모리에 접근할 때는 매우 빠릅니다. 하지만 CPU가 다른 소켓을 통해 로컬이 아닌 원격 메모리에 접근해야 하는 경우, 로컬 메모리보다 훨씬 느린 브릿지를 통해 데이터를 전송해야 하므로 엄청난 성능 저하가 발생합니다.
대부분의 워크로드 (workloads)에서는 이 문제가 거의 중요하지 않으며 아마 느끼지 못할 것입니다. 하지만 LLM 추론 성능은 메모리 대역폭 (memory bandwidth)에 크게 제한되기 때문에, 여러 개의 CPU를 사용하면서 각 토큰 (token)마다 대량의 원격 메모리를 읽어야 한다면 성능이 완전히 급락합니다.
이에 대한 일반적인 해결책은 llama.cpp에서 --numa isolate를 사용하는 것인데, 이는 모델/컨텍스트 (model/context) 데이터를 단일 소켓의 CPU와 메모리에 고정하여 원격 메모리 접근을 제거하지만, 이 경우 여러 개의 CPU를 사용하는 이점이 없으며 하나를 제외한 나머지는 유휴 (idle) 상태로 있게 됩니다.
이 포크 버전은 --numa mirror를 추가하여 모델 가중치 (model weights)와 KV 캐시 (KV cache)의 완전한 복제본을 만들어 모든 CPU 소켓이 노드 로컬 (node-local) 복사본을 갖도록 합니다. 이를 통해 추론 속도를 늦추는 대신, 모든 소켓에 걸친 모든 CPU 코어를 실제로 사용하여 추론 속도를 높일 수 있습니다.
트레이드오프 (trade-off)는 당연히 더 많은 메모리가 필요하다는 점입니다. CPU 소켓이 두 개라면 두 배의 RAM이 필요합니다.
제 벤치마크 (benchmarks) 결과는 상당한 이득을 보여주고 있습니다! 제 하드웨어는 다소 구형이지만, 최신 장비에서는 어떻게 작동하는지 알고 싶습니다.
테스트 설정
운영 체제 (Operating System):
Debian 13 "Trixie" (벤치마킹 중 numa_balancing 비활성화)
하드웨어 (Hardware):
모델: Dell PowerEdge R740
CPU: 2× Intel Xeon Gold 6248R (Cascade Lake), 2 NUMA 노드 (각 24 코어 / 48 스레드)
RAM: 768 GB RAM (노드당 384 GB) ECC DDR4 2400 MHz, 12개 메모리 채널 모두 사용
빌드 (Build): CPU 백엔드, Release, -DGGML_NATIVE=ON -DGGML_AVX512=ON -DGGML_AVX512_VNNI=ON. (VBMI/BF16은 활성화되지 않음 — Cascade Lake는 avx512_vbmi / avx512_bf16을 구현하지 않음.)
도구 (Tool): llama-bench, 결과당 3회 반복 (-r 3).
실행당 플래그 (Per-run flags): -rtr 1 -b 16 -ub 16 -p 512 -n 128 (런타임 리패킹 활성화; 배치 및 마이크로 배치 16; pp512 = 512 토큰의 프롬프트 처리 (prompt processing), tg128 = 128 토큰의 생성 (generation)).
비교 모드 (스레드 수는 -t/-tb에 대해 동일하게 설정):
isolate — --numa isolate -t 24 -tb 24 (소켓 1개 / 24 코어) — 싱글 소켓 기준선 (single-socket baseline)
mirror — --numa mirror -t 48 -tb 48 (두 소켓 모두 사용, 노드당 가중치 + KV 복제)
모든 처리량(throughput) 수치는 tokens/second입니다 (높을수록 좋습니다).
토큰 생성 (tg128)
모델 | isolate (1 소켓, 24t) | mirror (2 소켓, 48t) | mirror vs isolate
gemma-4-E2B (dense, Q5_K_M) | 47.20 | 62.00 | 1.31×
gemma-4-E4B (dense, Q5_K_M) | 23.77 | 33.62 | 1.41×
gemma-4-26B-A4B (MoE, UD-Q4_K_M) | 23.59 | 34.76 | 1.47×
Qwen3.6-27B (dense, Q4_K_M) | 5.27 | 8.32 | 1.58×
Qwen3.6-35B-A3B (MoE, UD-Q5_K_M) | 24.70 | 31.56 | 1.28×
Qwen3.5-122B-A10B (MoE, UD-Q3_K_XL) | 10.00 | 14.46 | 1.45×
프롬프트 처리 (pp512)
모델 | isolate (1 소켓, 24t) | mirror (2 소켓, 48t) | mirror vs isolate
gemma-4-E2B (dense, Q5_K_M) | 259.90 | 256.69 | 0.99×
gemma-4-E4B (dense, Q5_K_M) | 141.88 | 184.06 | 1.30×
gemma-4-26B-A4B (MoE, UD-Q4_K_M) | 143.41 | 201.69 | 1.41×
Qwen3.6-27B (dense, Q4_K_M) | 33.04 | 54.22 | 1.64×
Qwen3.6-35B-A3B (MoE, UD-Q5_K_M) | 153.68 | 193.21 | 1.26×
Qwen3.5-122B-A10B (MoE, UD-Q3_K_XL) | 57.17 | 83.01 | 1.45×
제출자: /u/_TheWolfOfWalmart_
[link] [comments]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/LocalLLaMA의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기