[LLM 추론] CPU 스레드와 NUMA 메모리 접근

GPU 자원이 부족한 환경에서 CPU·RAM·디스크의 역할을 구분하고, 스레드 수와 NUMA 메모리 배치가 추론 처리량에 미치는 영향을 설명합니다.

개요

큰 모델을 실행하려는데 GPU 자원, 특히 VRAM이 부족하면 서버에 남아 있는 CPU·RAM·디스크의 활용을 고려합니다.
목적은 GPU에 담기지 않는 모델을 실행하면서 가능한 처리량을 확보하는 것입니다.
다만 자원을 더 많이 쓰는 것과 더 빠르게 처리하는 것은 다릅니다.

RAM이 충분해 필요한 데이터가 메모리에 상주한다면 디스크는 주로 초기 적재에 관여합니다.
이때 디스크를 더 적극적으로 사용한다고 생성 TPS가 높아지는 것은 아닙니다.
이 문서는 이러한 혼합 실행에서 CPU를 늘렸을 때 효과가 달라지는 이유를 설명합니다. 현재 서버의 최적값을 확정한 실험 결과는 아닙니다.

로컬·원격 메모리 접근 차이

NUMA에서는 CPU가 접근하는 메모리 위치에 따라 지연과 대역폭 특성이 달라집니다.
일반적인 공유 메모리 NUMA 서버에서는 다른 노드의 RAM도 접근할 수 있습니다.
특정 CPU만 특정 RAM을 읽을 수 있다는 의미가 아닙니다.

CPU A 쪽 RAM에 데이터가 있는 상황을 단순화하면 다음과 같습니다.

CPU A ── 로컬 메모리 컨트롤러 ── RAM A
CPU B ── 노드 사이 연결 ── A 쪽 메모리 컨트롤러 ── RAM A

CPU B가 데이터를 읽지 못해서 느린 것이 아닙니다.
읽을 수는 있지만 더 먼 경로와 공유 자원을 사용하기 때문에 비용이 추가될 수 있습니다.
이 구조는 Linux 커널 NUMA 문서에서 설명합니다.

코어 확장의 한계

데이터를 RAM A에 둔 채 계산할 CPU만 늘리는 상황을 생각해볼 수 있습니다.
추가 코어가 효과를 내려면 데이터를 공급하는 경로에도 여유가 있어야 합니다.

  1. CPU A의 코어들이 RAM A에서 데이터를 읽고 계산합니다.
  2. RAM A의 메모리 대역폭이 포화되면 코어를 추가해도 데이터 공급량은 충분히 늘지 않습니다.
  3. CPU B까지 참여하면 원격 접근이 추가됩니다. 노드 사이 연결과 스레드 동기화가 새로운 비용이 될 수 있습니다.

반대로 계산량이 병목이라면 코어 추가가 도움이 됩니다.
데이터와 작업을 여러 노드에 적절히 분산하면 여러 메모리 채널을 활용할 여지도 있습니다.
따라서 전체 CPU 사용과 한 노드 고정 중 어느 쪽이 항상 빠르다고 정할 수는 없습니다.

CPU 고정과 메모리 배치의 구분

CPU 실행 위치를 제한해도 데이터가 그 CPU 가까이에 자동으로 재배치되는 것은 아닙니다.
실행 스레드 수, 실행 위치, 메모리 할당 정책은 각각 다른 설정입니다.

설정제어하는 대상주의할 점
CPU threads계산에 참여할 스레드 수허용된 CPU 범위를 넓히지는 않습니다.
taskset실행 가능한 논리 CPU 집합메모리 배치를 지정하지 않습니다.
NUMA 메모리 정책새 메모리를 할당할 노드와 방식기존 페이지가 모두 이동하는 것은 아닙니다.

예를 들어 32개 논리 CPU만 허용한 상태에서 계산 스레드를 128개로 늘려도 128개 CPU를 사용하는 것은 아닙니다.
같은 CPU를 더 많은 스레드가 나눠 쓰면서 비용이 커질 수 있습니다.

메모리를 특정 노드에 강하게 제한하려면 해당 노드의 여유 용량도 확인해야 합니다.
서버 전체 RAM이 충분해도 허용된 노드의 메모리가 부족하면 할당에 실패할 수 있습니다.
정책의 적용 범위와 할당 동작은 Linux NUMA 메모리 정책 문서를 참고합니다.

CPU·GPU 혼합 실행의 병목

GPU와 시스템 RAM을 함께 사용하는 실행에서는 CPU 계산 외에도 가중치 읽기와 GPU 전송이 시간을 차지할 수 있습니다.
구현에 따라 RAM의 데이터를 CPU가 계산하거나 GPU로 전달합니다. GPU 캐시에 이미 있는 가중치는 다른 경로를 사용할 수 있습니다.

이는 병목의 후보이며 현재 서버에서 측정으로 확정한 원인은 아닙니다.
CPU 사용률이 낮다는 관측 하나만으로 코어가 부족한지, 메모리를 기다리는지, GPU를 기다리는지 구분할 수 없습니다.

성능 차이와 적용 범위

CPU 스레드를 무조건 늘려도 빨라지지 않는다는 주장은 llama.cpp 공식 성능 안내에서도 확인할 수 있습니다.
안내에서는 과도한 스레드 수를 점검하고 적은 수에서 늘려가며 성능이 떨어지는 지점을 찾도록 설명합니다.
다만 이 설명이 현재 서버에서 CPU가 병목이라는 증거는 아닙니다.

공식 문서의 예시는 A6000 48GB, 물리 CPU 7코어, RAM 32GB, 30B Q4 모델 조건입니다.
같은 GPU 오프로딩 옵션에서 스레드 수를 바꾼 결과는 다음과 같습니다.

CPU 스레드생성 속도
15.5 tokens/s
49.1 tokens/s
78.7 tokens/s

이 사례에서는 1→4스레드가 약 65% 빨랐지만 4→7스레드는 약 4% 느렸습니다.
코어 추가의 효과가 일정하지 않다는 근거이며, NUMA 효과를 분리한 실험은 아닙니다.
오래된 별도 모델·하드웨어의 결과이므로 현재 DeepSeek 구성의 예상 개선율로 사용할 수 없습니다.

현재 구성의 차이는 CPU·메모리 경로가 전체 시간에서 차지하는 비중에 달려 있습니다.
예를 들어 서로 겹치지 않는 처리 시간 중 20%인 구간만 두 배로 빨라진다면 전체 속도는 약 1.11배입니다.
같은 조건에서 개선 구간이 80%라면 약 1.67배가 됩니다. 이는 영향 범위를 설명하는 계산 예시이며 실측이나 예측값이 아닙니다.

따라서 CPU·RAM 접근이 주된 병목이면 차이가 클 수 있습니다.
GPU 계산이나 전송이 시간을 대부분 차지한다면 CPU 스레드 조정만으로 얻는 효과는 작을 수 있습니다.
NUMA 접근 특성도 플랫폼마다 다르므로 일률적인 배수를 붙이지 않습니다. Linux NUMA 메모리 성능 문서는 지연과 대역폭을 별도 특성으로 다룹니다.

성능 비교 기준

NUMA 최적화의 목적은 CPU 사용률을 높이는 것이 아니라 요청 처리 시간을 줄이는 것입니다.
같은 조건에서 실행 위치와 스레드 수를 바꾸고 실제 처리량을 비교해야 합니다.

CPU 사용률만 높아지고 TPS가 그대로라면 해당 조건에서는 코어를 추가해도 실익이 작습니다.
NUMA 최적화는 계산하는 CPU와 필요한 데이터의 위치를 함께 맞추는 작업입니다.

참고 자료