LLM INFERENCE · CPU–GPU HYBRID
CPU MoE Offloading:
GPU 메모리의 한계를 넘으면 무엇이 달라질까
Hot expert는 GPU에, Cold expert는 Host Memory에: 실행 구조와 기본 성능, 최적화의 출발점
큰 언어 모델을 실행할 때는 연산 속도에 앞서 가중치와 실행 상태를 GPU 메모리에 담을 수 있는지부터 확인해야 한다. MoE 모델은 토큰마다 일부 전문가만 계산하지만, 선택 가능한 전문가의 가중치를 실행 가능한 위치에 보관해야 한다. 모든 가중치를 고속 GPU 메모리에 두기 어렵다면, 같은 서버의 CPU 메모리를 함께 사용하는 방법이 있다.
Hot expert는 GPU 메모리에 남겨 GPU가 계산하고, Cold expert는 Host Memory에 두어 CPU가 계산한다. 이 글에서 다루는 CPU MoE Offloading은 이런 부분 상주 방식이다. GPU는 Attention과 Router, 자주 선택되는 전문가를 처리하고, CPU는 선택된 Cold expert를 실행한다. 관심사는 단순히 모델을 적재하는 것이 아니라, 두 장치가 계산을 나누면서 생기는 전달 비용과 결과 대기를 얼마나 줄일 수 있느냐다.
(프롬프트)
(토큰 스트림)
& Scheduler
(Attention + Dense)
(Top-K)
(GPU, FP8)
(CPU, INT4)
• 자주 선택되는 전문가
• FP8 전문가 가중치
• INT4 전문가 가중치
• CPU DRAM 2 TB
구조도 확대 보기
1. 일부만 계산해도, 전문가 가중치는 필요하다
MoE(Mixture of Experts)는 신경망의 일부 연산을 여러 전문가 신경망으로 나누고, Router가 토큰의 중간 표현을 보고 사용할 전문가를 선택하는 구조다. 선택된 전문가들의 출력은 라우팅 가중치에 따라 합쳐진다. 전체 파라미터 수가 가중치 보관 공간과 관련된다면, 활성 파라미터 수는 토큰 하나의 계산량과 관련된다.
실험에 사용한 Qwen3-Coder-480B-A35B-Instruct-FP8은 층마다 160개 전문가 중 토큰별로 8개를 선택한다. 한 토큰에서 사용하지 않은 전문가도 다른 토큰에서는 필요하다. 일부만 계산한다는 이유로 나머지 가중치를 버릴 수는 없다. [1]
Hot과 Cold는 모델에 고정된 두 종류의 전문가가 아니라, 선택 빈도와 배치 정책에 따른 구분이다. 자주 선택되는 전문가를 GPU에 남기고 나머지를 CPU가 맡는다. Cold expert도 선택되면 계산해야 하며, 중요하지 않은 연산이라는 뜻은 아니다. 또한 GPU에 전문가 96개를 둔다고 해서 토큰별 8개 선택 중 일정한 수가 반드시 GPU나 CPU에 배정되는 것은 아니다.
GPU의 고대역폭 메모리인 HBM에는 가중치뿐 아니라 KV cache도 들어간다. KV cache는 이미 처리한 토큰의 Attention 상태를 보관하는 공간이다. 문맥이 길어지거나 동시 요청이 늘면 캐시와 실행 버퍼의 공간 요구도 커진다.
Hot expert 가중치 + Attention·Router 등 비전문가 가중치 + KV cache + 활성값·실행 버퍼
따라서 Hot expert를 가능한 한 많이 올리는 것만으로는 충분하지 않다. GPU에서 직접 계산할 전문가와 요청을 유지할 캐시 사이에 공간을 나누어야 한다. 전문가 배치와 서비스가 요구하는 문맥 길이·동시성을 함께 결정해야 하는 이유다.
2. CPU에 보관하는 것과 CPU가 계산하는 것은 다르다
CPU offloading이라는 이름 아래에도 서로 다른 실행 방식이 있다. 가장 중요한 차이는 전문가 가중치를 GPU로 가져와 계산하는지, CPU가 자신의 메모리를 읽어 직접 계산하는지다.
| 방식 | 전문가 계산 주체 | CPU–GPU 간 주요 전달 데이터 |
|---|---|---|
| GPU 전용 실행 | GPU가 모든 선택 전문가를 계산 | CPU 전문가 연산을 위한 왕복 없음 |
| CPU 저장·GPU 계산 | 필요한 가중치를 GPU로 가져온 뒤 계산 | 전문가 가중치 |
| Hot·Cold 부분 상주 | GPU는 Hot, CPU는 Cold를 계산 | Cold 경로의 활성값·선택 정보·연산 결과 |
부분 상주 방식에서는 GPU의 Hot expert와 CPU의 Cold expert가 서로 다른 입력 집합을 처리한다. CPU에 필요한 활성값, 전문가 ID, 라우팅 가중치를 전달하고 연산 결과를 받는다. Cold expert의 전체 가중치를 토큰마다 GPU로 옮기지 않는다. 대신 CPU의 연산 속도와 메모리 대역폭, 결과를 돌려주는 지연이 중요해진다.
3. 같은 서버에서 나뉘고 다시 합쳐지는 실행
아래 구조는 CPU 2소켓과 GPU 4장이 하나의 모델을 실행하는 구성이다. Hot expert와 Attention·Router·KV cache는 GPU에, Cold expert 가중치는 Host Memory에 둔다. 그림의 TP4는 텐서 병렬로 모델의 행렬 연산을 GPU 4장에 나눈다는 뜻이다. 전문가를 CPU와 GPU 중 어디에 둘지 결정하는 배치 정책과는 별개의 축이다.
model_path
kt_weight_path
읽기
hidden state
MoE 입력 준비
top-8 → Hot / Cold 분기
GPU 연산 · FP8
잔차 연결 → 다음 층
구조도 확대 보기
가중치 적재와 추론 중 데이터 이동을 구별한다
기동할 때는 원본 체크포인트에서 GPU용 가중치를, 변환된 전문가 파일에서 CPU용 가중치를 읽는다. 실험의 GPU 전문가 경로는 8비트 부동소수점인 FP8, CPU 경로는 4비트 정수 양자화인 INT4를 사용한다. 정밀도 표기는 전문가 가중치 경로를 가리키며, 모든 모듈이 같은 정밀도로 계산된다는 뜻은 아니다. [2]
그림의 점선은 가중치 적재·참조와 별도로 표시한 잔차 입력이고, 색 실선은 추론 중 활성값·선택 정보·결과의 이동이다. CPU와 GPU는 별도 서버가 아니라 동일한 물리 서버의 구성요소다.
① 입력 준비. GPU가 Attention을 수행하고 전문가 연산에 필요한 활성값을 준비한다. Attention의 KV cache는 GPU 메모리에서 접근한다.
② 선택과 분기. Router가 사용할 전문가 ID와 라우팅 가중치를 계산한다. 실행기는 배치표를 조회해 선택된 전문가를 GPU 경로와 CPU 경로로 나눈다. Router의 선택 정보와 전문가에 입력할 활성값은 서로 다른 데이터다.
③ 전문가 계산. GPU는 자신의 메모리에 있는 Hot expert를 계산한다. CPU는 전달받은 입력으로 Host Memory의 Cold expert를 계산한다. CPU 쪽 전문가 실행기는 96개 워커를 두 스레드 풀에 나누어 사용한다. 소켓마다 가까운 메모리와 코어를 활용하는 NUMA 배치도 이 경로에 속한다.
④ 결과 결합. CPU에서 돌아온 부분 결과와 GPU의 Hot expert 부분 결과를 GPU에서 결합한다. 각 전문가의 라우팅 가중치와 기여분을 중복 적용하지 않아야 하며, 잔차 입력을 더한 뒤 다음 층으로 진행한다.
두 장치의 독립적인 계산을 겹쳐 실행할 수 있어도, 다음 연산이 필요로 하는 결과가 모이는 지점은 남는다. 전송을 비동기로 만들었다고 이 의존 관계가 없어지는 것은 아니다.
4. Hot expert를 GPU에 둔 기본 구성의 실험 결과
측정 대상은 Qwen3-Coder-480B-A35B-Instruct-FP8이다. 같은 서버에서 H100 80 GB 4장과 Xeon Platinum 8480+ 2개, DDR5 2 TB를 사용했다. 각 층의 160개 전문가 가운데 Hot expert 96개를 GPU에, 나머지 64개를 Host Memory에 배치했다. GPU 담당 전문가의 가중치는 4장에 텐서 병렬로 나누고, CPU가 Cold expert를 직접 계산한다. [2]
이 구성은 층별 전문가 수 재배분, 작업 전달 방식 개선, 추가 CPU 커널 최적화의 출발점으로 측정한 기본 부분 상주 구성이다. 이미 선택 빈도를 반영한 전문가 배치와 아래 실행 설정을 사용하므로, 아무 설정도 조정하지 않은 상태를 뜻하지는 않는다.
| 항목 | 부분 상주 측정 설정 |
|---|---|
| GPU·CPU 역할 | GPU 4장: Hot expert·Attention·Router / CPU 2소켓: Cold expert |
| 전문가 배치 | 62개 층 각각 GPU 96개·CPU 64개 / 토큰별 선택 8개 |
| CPU 실행 | 워커 96개, 스레드 풀 2개 / CPU 2.0 GHz, 터보 비활성 |
| 서빙 소프트웨어 | SGLang 0.5.18 + kt-kernel 0.7.0.post1 / 실험용 수정 빌드 |
| KV cache·실행 옵션 | BF16 정밀도, 총 40,960토큰 용량 / CUDA graph 배치 크기 최대 64 / 전문가 지연 처리 한도 토큰당 4 |
입력은 Sonnet 데이터셋으로 512토큰 입력·128토큰 출력, 동시 요청 64, 총 256요청을 설정했다. 공통 접두사 길이는 0으로 두고 매 반복 전에 캐시를 초기화했으며, 종료 토큰에 의한 조기 종료 없이 출력 길이를 고정했다. 같은 조건에서 3회 반복했다.
비교 대상은 동일 모델의 GPU 8장 전용 실행이다. CPU 전문가 계산 없이 GPU에서 모든 선택 전문가를 처리하며, 텐서 병렬과 전문가 병렬을 사용했다. 캐시 용량은 131,072토큰이었다. 두 구성 모두 SGLang으로 서빙하고, 같은 요청 조건에서 측정했다.
| 지표 | GPU 4장 + CPU 부분 상주 기본 구성 | GPU 8장 GPU 전용 실행 |
|---|---|---|
| 출력 처리량 · 평균 ± 표본표준편차 | 612.11 ± 6.76 토큰/초 | 1,703.56 ± 4.93 토큰/초 |
| 첫 토큰까지의 시간 · 95백분위수 | 4,195 ms | 1,464 ms |
| 첫 토큰 이후 토큰당 생성 시간 · 95백분위수 | 91.0 ms | 32.8 ms |
| 완료 / 실패 요청 · 반복마다 | 256 / 0 | 256 / 0 |
처리량의 ±는 3회 반복의 표본표준편차다. 지연 수치는 각 반복에서 구한 요청별 95백분위수를 평균한 값이며, 세 반복의 모든 요청을 합쳐 다시 계산한 백분위수가 아니다. 95백분위수는 느린 쪽 5%의 경계를 나타낸다. [2]
이 결과가 보여 주는 것
Hot·Cold 분담 구성은 GPU 4장으로 모델을 실행해 요청을 완료했지만, GPU 8장 전용 실행보다 처리량이 낮고 요청 지연이 길었다. 필요한 GPU 수를 줄인 것과 같은 응답 속도를 확보한 것은 별개의 결과다.
이 차이를 CPU 오프로딩만의 손실로 단정할 수는 없다. GPU 수, 전문가 정밀도, 병렬 구성과 캐시 용량이 함께 다르다. 또한 612.11토큰/초는 모든 요청이 생성한 토큰을 합친 처리량이지, 사용자 한 명의 생성 속도가 아니다. 요청 완료만으로 출력 정확성이나 장시간 운영 안정성이 검증되는 것도 아니다.
이 측정은 Hot·Cold 분담이 작동하는 기준점이다. 다음 질문은 같은 GPU 4장과 출력 조건을 유지하면서 어느 부분의 비용을 줄일 것인가다.
5. 사용률보다 실행 구간을 나누어 봐야 한다
CPU 사용률이 높다고 유효한 전문가 연산으로 포화된 것은 아니다. 작업 전달과 완료 확인, 폴링에도 CPU 시간이 사용된다. 논리 스레드 전체의 평균은 실제로 계산하는 코어의 상태를 가릴 수 있다. GPU 사용률 역시 Cold 결과를 기다린 시간과 GPU 자체 연산 시간을 직접 분리해 주지는 않는다.
공통: GPU 입력 준비·Router 선택
Hot: GPU 전문가 계산
Cold: 입력 전달 → CPU 전문가 계산 → 결과 반환
합류: 두 경로 완료 확인 → 결과 결합·다음 층 진행
확인할 것은 CPU가 메모리를 읽느라 늦는지, 작은 작업을 넘기는 관리 비용이 큰지, 이미 준비된 결과를 GPU가 늦게 받아 가는지다. 같은 이유로 GPU 메모리 총사용량만 보지 말고 Hot expert 가중치, 캐시, 실행 버퍼의 점유를 구분해야 한다. GPU 전력이 낮아지더라도 CPU 연산과 전체 실행 시간이 늘었다면 에너지 효율이 좋아졌다고 단정할 수 없다.
[1] Qwen3-Coder-480B 모델 설정 및 전문가 배치 기록, 2026-09-15. 62개 층·층당 160개 전문가·토큰별 선택 8개.
[2] Qwen3-Coder-480B 부분 상주·GPU 전용 비교 실험 기록, 2026-09-16. 실행 명령, 반복별 성능과 집계, 캐시 정책 및 지표 계산 규칙. 매 반복의 실제 입력·출력 합계는 두 구성 모두 129,237 / 32,768토큰이다.
6. 최적화는 배치·전달·계산을 함께 다룬다
목표는 CPU를 더 바쁘게 만드는 것이 아니라, 출력의 정확성을 유지하면서 Cold 경로의 계산량과 결과 대기를 줄이는 것이다. 기본 구성에서 확인할 최적화 지점은 다음과 같다.
같은 GPU 공간으로 더 효과적인 전문가를 배치한다
모든 층에 96개씩 동일하게 남기는 대신, 실제 요청의 층별 선택 빈도와 Cold 경로 지연을 살펴볼 수 있다. 자주 사용되거나 CPU에서 계산할 때 다음 단계를 오래 기다리게 하는 전문가에 GPU 공간을 우선 배분하는 방식이다. 단순 적중률보다 실제 결과 대기시간이 얼마나 줄었는지를 확인해야 한다.
Hot expert와 KV cache의 공간을 함께 조정한다
Hot expert 수를 늘리면 CPU 계산은 줄어들 수 있지만 캐시와 실행 버퍼의 여유도 줄어든다. 필요한 문맥 길이와 동시 요청을 유지할 수 있는 범위에서 조정하고, 처리량뿐 아니라 첫 토큰 지연과 생성 중 지연을 함께 비교한다. 캐시 부족으로 대기나 재처리가 늘면 전문가를 더 올린 이득이 사라질 수 있다.
작업 전달과 완료 확인의 반복 비용을 줄인다
CPU에 작은 전문가 작업을 요청하는 과정이 여러 층에서 반복되면 관리 비용도 누적된다. 입력·결과 버퍼 재사용, 불필요한 콜백과 폴링 감소, 실제 계산할 항목이 없는 작업의 생략을 검토할 수 있다. 다만 빈 작업을 생략하는 것과 선택된 전문가 계산을 생략하는 것은 다르다. 완료 신호와 결과 버퍼의 수명이 정확히 유지돼야 한다.
CPU 커널과 메모리 배치를 실제 작업 크기에 맞춘다
동시 요청이 64개여도 토큰이 여러 전문가로 흩어지면 각 전문가가 받는 입력 행 수는 작을 수 있다. 이 분포에 맞춰 CPU의 행렬·벡터 연산 경로, 작업 묶음과 워커 수를 선택해야 한다. 소켓별 메모리 접근을 조정할 때도 원격 접근을 줄이는 이득과 사용할 수 있는 코어·메모리 채널이 줄어드는 손해를 함께 본다.
입력 처리와 생성, 정확성과 안정성을 따로 검증한다
긴 입력을 한꺼번에 처리하는 단계와 한 토큰씩 생성하는 단계는 작업 크기가 다르다. 입력 처리 묶음, 동시 요청 제한, GPU 실행 그래프 범위를 조정할 때 두 단계의 지연을 나누어 봐야 한다. 캐시를 재사용한 요청과 캐시를 초기화한 요청도 분리한다. 배치·정밀도·커널·지연 처리 방식을 바꿀 때는 동일 입력의 정답과 출력 변화, 수치 오류와 장시간 실행 실패를 확인해야 한다.
Hot expert는 GPU에, Cold expert는 Host Memory에 둔다. 그다음의 성능은 어느 전문가를 남기고, 두 실행 경로가 얼마나 효율적으로 계산하고 합류하는지에 달려 있다. 배치와 캐시 예산을 정한 뒤 전달·계산·대기를 구분해 줄이는 것이 CPU MoE Offloading 최적화의 출발점이다.