380. NUMA (Non-Uniform Memory Access)

핵심 인사이트 (3줄 요약)

  1. 본질: 공유 메모리 시스템(SMP)에서 CPU 코어 수가 기하급수적으로 늘어날 때 발생하는 치명적인 시스템 버스 병목을 해결하기 위해, 메인 메모리(RAM)를 물리적으로 쪼개어 각 CPU 소켓 바로 옆에 전속으로 붙여버린 '불균일' 아키텍처다.
  2. 가치: 내 소켓 옆에 붙은 메모리(Local Memory)를 읽을 때는 버스 쟁탈전 없이 광속으로 처리하고, 부득이하게 남의 소켓 메모리(Remote Memory)를 읽을 때만 내부 통신망(QPI/UPI)을 거치는 타협을 통해 64~128코어 이상의 초거대 엔터프라이즈 서버를 제작할 수 있게 되었다.
  3. 융합: 하드웨어는 물리적으로 불균등해졌지만 프로그래머에게는 여전히 하나의 거대한 메모리(단일 주소 공간)로 보이게 덮어주며, 성능 누수를 막기 위해 OS 레벨의 지능형 스케줄링(NUMA-Aware 스케줄러) 및 스레드-메모리 바인딩(Pinning) 기술과 치열하게 융합해야 한다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

NUMA (Non-Uniform Memory Access, 불균일 메모리 접근) 아키텍처는 UMA(균일 메모리 접근)의 아름답고 평등한 유토피아가 잔인한 물리학의 한계에 부딪혀 무너지면서 탄생한 현실적 타협안이다.

과거에는 CPU 4개가 1개의 메모리로 통하는 단 하나의 좁은 고속도로(시스템 버스)를 공유하는 UMA 방식을 썼다. 하지만 서버가 진화하여 CPU 코어가 16개, 32개, 64개로 늘어나자 64마리의 맹수가 하나의 길목으로 쏟아지며 극심한 병목(Bus Contention)이 터졌다. 64코어를 사서 꽂았는데 성능은 8코어 시절보다 안 나오는 기괴한 역전 현상이 일어난 것이다.

엔지니어들은 결단했다. "중앙에 있는 거대한 메모리를 다 박살 내서 4조각으로 쪼갠 뒤, 각각의 CPU 소켓 바로 옆에 개인 텃밭처럼 달아주자. 단, 남의 텃밭에 가는 길(인터커넥트)은 열어두어 논리적으로는 하나의 메모리처럼 보이게 속이자!"

[UMA의 한계와 NUMA 아키텍처에 의한 병목 돌파 도식]

(A) 과거의 UMA 모델 (공산주의식 중앙 통제)
[ CPU 0 ]  [ CPU 1 ]  [ CPU 2 ]  [ CPU 3 ]
    │          │          │          │
   === [ 단일 시스템 버스 (트래픽 지옥 발생!) ] ===
                   ▼
         [ 거대한 중앙 메모리 ]

(B) 현대의 NUMA 모델 (지방 자치제 분권화)
  [ CPU 0 ] <====== (QPI 고속망) ======> [ CPU 1 ]
      │ (Local: 10ns 광속)                    │ (Local: 10ns 광속)
      ▼                                       ▼
[ Memory 0 ]                            [ Memory 1 ]

* 핵심 차이: 
CPU 0이 자기 옆에 있는 Memory 0을 읽으면 10ns 만에 즉각 반응한다 (Local Access).
하지만 CPU 0이 옆집 Memory 1의 데이터를 읽으려면 CPU 1을 거쳐 통신망을 타야 하므로 30ns가 걸린다 (Remote Access).
= "메모리 위치에 따라 접근 속도가 다르다(Non-Uniform)"는 현상이 발생함!

이 불평등한 구조 덕분에, 각 CPU는 남들 눈치를 안 보고 자기 옆의 메모리를 미친 듯이 파먹으며 무한한 연산력을 뿜어낼 수 있게 되었다. 즉, 확장성(Scalability)을 얻기 위해 메모리 접근의 평등성(Uniformity)을 제물로 바친 아키텍처다.

📢 섹션 요약 비유: 중앙의 큰 공용 세탁실(UMA)에 64세대가 몰려 폭동이 나자, 아파트의 각 층마다 작은 세탁기(NUMA)를 놔준 것입니다. 내 층의 세탁기를 쓰면 1분 컷(Local)이지만, 만약 고장 나서 윗층 세탁기를 쓰려면 계단을 올라가는 수고(Remote 지연)가 드는 불균일한 구조입니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

NUMA 아키텍처를 지탱하는 핵심은 메모리가 물리적으로 쪼개져 있음에도 소프트웨어(OS와 프로그램)에게는 하나의 0번지~100GB번지로 쭉 이어진 평탄한 주소 공간(Global Address Space) 인 것처럼 완벽한 마술(사기)을 부리는 하드웨어 연결망이다.

구성 요소역할 및 한계아키텍처적 트레이드오프비유
NUMA Node (노드)1개의 CPU(코어 그룹) + 바로 직결된 1개의 로컬 메모리 묶음내 노드 안에서의 연산은 우주 최강의 속도 보장자급자족이 가능한 지방 행정 구역
Local Memory내 소켓에 직접 연결된 램CPU 대기 시간(Stall) 0에 수렴내 책상 서랍
Remote Memory다른 소켓 쪽에 꽂혀있는 램통신망(홉, Hop)을 거쳐야 하므로 거리에 따라 지연시간(Latency Penalty) 2~3배 폭증옆 부서 팀장의 서랍
Interconnect (UPI/QPI)노드와 노드 사이를 연결해 남의 메모리를 읽게 해주는 브리지 망Intel의 UPI, AMD의 Infinity Fabric. 대역폭 한계로 잦은 핑 퐁 시 마비됨도시간 고속도로 통신망
ccNUMA (Cache-Coherent NUMA)물리적으로 쪼개진 램에서도 캐시 일관성 보장내가 A변수를 고치면 다른 노드의 캐시도 알아서 무효화(Invalidate)되는 디렉터리 기반 마법 로직전국 주민등록망 자동 갱신 시스템

현대 서버의 가장 큰 성능 절벽은 **'원격 메모리 접근 (Remote Access)'**에서 발생한다.

[NUMA 홉(Hop) 거리에 따른 치명적 지연시간 페널티 프랙탈]

[Node 0] --(1 홉)-- [Node 1] --(1 홉)-- [Node 2]

1. Node 0의 CPU가 Node 0의 Memory 읽음 -> (Local Access) : 지연시간 1배 (기준)
2. Node 0의 CPU가 Node 1의 Memory 읽음 -> (1 Hop Remote)  : 지연시간 1.5배 느려짐
3. Node 0의 CPU가 Node 2의 Memory 읽음 -> (2 Hop Remote)  : 지연시간 2.5배 느려짐! (극혐)

=> 아키텍처적 결론: 개발자가 짜 놓은 스레드가 만약 "데이터는 Node 2 램에 있는데 연산은 Node 0 코어에서" 
   하는 끔찍한 배치(Thread Migration)를 겪게 되면, 서버는 최고가 장비를 쓰고도 펜티엄급 속도를 내게 된다.

이 현상을 막기 위해 NUMA 아키텍처는 하드웨어 혼자서는 절대 살 수 없고, 똑똑한 운영체제(NUMA-Aware OS)의 강제적인 통제와 융합되어야만 진정한 성능을 뿜어낸다.

📢 섹션 요약 비유: NUMA의 통신망은, 내 책상에서 지우개를 찾는 것(로컬)은 1초면 되지만, 부장님 책상에서 결재 서류를 가져오는 것(1 홉 리모트)은 눈치가 보여 3초가 걸리고, 옆 건물 사장님실에서 가져오는 것(2 홉 리모트)은 10초가 걸리는 심리적/물리적 거리의 페널티와 완벽히 같습니다.


Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)

NUMA가 서버 시장을 장악한 이유는 공유 메모리(UMA)의 편리함과 분산 메모리(Cluster)의 확장성을 교묘하게 융합한 "중도 타협" 노선을 택했기 때문이다.

플린의 하위 메모리 아키텍처 타협의 진화 곡선

항목UMA (순수 대칭 공유)NUMA (불균일 타협)Cluster (완전 분산 찢기)
메모리 위치물리적 중앙집중 (1개)물리적 분산, 논리적 단일화물리적, 논리적 완전 분리
코딩 난이도100% 맘 편함신경 안 써도 되지만, 튜닝 안 하면 성능 폭망MPI 패싱으로 극악의 난이도
확장 한계점최대 ~32코어수백 ~ 수천 코어 (현대 랙 서버 타겟)수만 대 무한 확장 (AWS 클라우드)
하드웨어 제어버스 스누핑 의존Directory 기반 캐시 코히런스 (ccNUMA)없음. 소프트웨어가 전담

타 과목 관점의 융합 시너지

  • 운영체제 (NUMA-Aware 스케줄러 & First-Touch 정책): 초창기 멍청한 OS는 UMA 시절의 버릇을 못 버리고, CPU 코어의 부하를 맞추겠다며 스레드를 Node 0에서 Node 1로 계속 튕겨냈다(Migration). 그 결과 스레드가 자기 램과 생이별하여 엄청난 딜레이가 폭발했다. 현대 리눅스 커널은 **"처음 메모리를 할당(malloc)할 때 터치한 CPU의 로컬 NUMA 노드에 메모리를 할당한다 (First-Touch Policy)"**는 원칙과, 웬만하면 스레드를 다른 노드로 이주시키지 않는 NUMA-Aware 스케줄러로 진화하여 하드웨어와 완벽한 시너지를 이룬다.
  • 가상화 (Hypervisor / vNUMA): 클라우드 컴퓨팅(KVM, VMware) 환경에서 물리 서버는 거대한 NUMA 덩어리다. 사용자가 16코어짜리 가상 머신(VM)을 만들 때, 하이퍼바이저가 아무 생각 없이 Node 0에서 8코어, Node 1에서 8코어를 잘라서 붙여주면, 이 VM은 내부적으로 통신할 때마다 하드웨어 병목에 시달린다. 이를 막기 위해 VM 안의 게스트 OS에게도 "너의 밑바닥은 NUMA 구조야"라고 핀(topology)을 그대로 노출시켜 주는 vNUMA (Virtual NUMA) 기술이 필수적인 가상화 클라우드 표준 융합 기술이 되었다.
[운영체제(Linux)의 NUMA 극복 메모리 할당 전략 융합]

[ 개발자 코드 ]
void* buffer = malloc(10GB);  <-- (주의! 이 순간에는 물리 램이 할당되지 않음)
// ...
for(int i=0; i<10GB; i++) buffer[i] = 0; <-- 이 첫 번째 쓰기(First-Touch) 순간에 진짜 할당됨!

* OS의 영악한 배치 판단:
"지금 이 for문을 실행하는 놈이 CPU 1이네? 
그럼 이 10GB 버퍼는 무조건 [Memory 1(CPU 1의 로컬 램)] 에 몰빵해서 깔아준다!"
=> 이후 CPU 1이 이 데이터를 쓸 때 영원히 Local 속도(광속)를 유지하게 만드는 마법.

📢 섹션 요약 비유: NUMA-Aware OS의 First-touch 정책은, 회사에서 직원이 처음 입사해 "저 펜 좀 쓰겠습니다"라고 손을 대는 순간, 총무팀(OS)이 그 직원의 책상 첫 번째 서랍(로컬 메모리)에 펜을 가득 채워주어 다시는 남의 서랍(리모트)을 뒤지지 않게 배려하는 최고급 복지 시스템입니다.


Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)

현장의 백엔드 엔지니어나 DBA는 2 CPU (2소켓) 이상의 거대한 서버를 만질 때 닥칠 끔찍한 성능 저하의 주범인 NUMA 핑퐁(Ping-pong)을 진단하고, OS 명령어로 이 를 찢어 발겨야 한다.

실무 성능 최적화 및 커널 튜닝 시나리오

  1. RDBMS (MySQL, PostgreSQL) 메모리 파편화 및 OOM(Out of Memory) 방어

    • 상황: 256GB 메모리를 가진 2소켓 서버에서, 전체 메모리 사용량이 150GB밖에 안 되는데 DB 데몬이 자꾸 메모리 부족(OOM Killer)으로 사망함.
    • 의사결정: numactl --interleave=all 명령어로 DB 서비스를 강제 기동하여, OS의 First-Touch 몰빵 정책을 끄고 메모리를 모든 노드에 지퍼백처럼 1:1로 골고루 분산 할당하도록 튜닝한다.
    • 이유: DB는 엄청나게 거대한 단일 힙(Buffer Pool) 공간을 먼저 할당해 버린다. 이때 First-Touch 정책에 의해 Node 0에 128GB가 꽉 차게 할당되어 버리면, 전체 메모리는 남았지만 Node 0 램이 고갈되었다는 이유로 리눅스가 DB를 OOM으로 쏴 죽여버린다. 데이터베이스 같은 거대 공유 애플리케이션은 NUMA를 UMA처럼 강제로 비틀어(Interleave) 속도를 약간 손해보더라도 메모리 용량의 불균형 폭발을 막는 것이 실무의 정석이다.
  2. 초저지연 (Ultra-Low Latency) C/C++ 게임 서버 / HFT 트레이딩 코딩

    • 상황: 증권사 서버에서 특정 스레드의 틱(Tick) 처리 속도가 가끔씩 30ns에서 150ns로 5배 이상 튐(Jitter 발생).
    • 의사결정: 소스 코드 레벨에서 스레드와 메모리를 묶어버리는 numa_alloc_onnode() API를 사용하고, 스레드를 특정 코어에 못 박는 pthread_setaffinity_np (CPU Pinning) 기법을 강제 적용한다.
    • 이유: OS 스케줄러가 아무리 똑똑해도 간혹 부하 분산을 위해 스레드를 남의 노드로 휙 던져버리는(Migration) 짓을 한다. 나노초가 돈과 직결되는 HFT나 게임 서버의 락-프리(Lock-free) 폴링 스레드는 코드가 죽을 때까지 절대 한 코어와 한 로컬 메모리를 벗어나지 못하도록 족쇄(Affinity)를 채워야만 극단적인 Jitter를 억제할 수 있다.
[실무 서버 NUMA 병목 진단 및 처방 트리]

[현상] 리눅스 쉘 명령어 `numastat -m` 결과 확인
 ├─ Node 0 메모리는 100% 꽉 차있고, Node 1 메모리는 텅텅 비어 있는가?
 │   ├─ Yes ──> NUMA 불균형 병목 확정! 
 │   │          => 해결: DB나 JVM 구동 시 메모리를 라운드로빈으로 뿌리는 
 │   │                   Interleave 정책 적용. (성능은 평준화되나 OOM 생존 보장)
 │   │
 │   └─ No ───> 둘 다 골고루 차있다.
 │               ▼
 ├─ `perf c2c` 또는 `numastat` 결과 원격 접근(Remote Node Access) 횟수가 로컬 접근을 역전했는가?
 │   ├─ Yes ──> 스레드가 남의 램을 파먹느라 QPI 버스가 미어터지는 중.
 │   │          => 해결: Taskset, CPU Pinning으로 스레드가 자기 집을 못 떠나게 묶어둘 것.
 │   └─ No ───> NUMA 튜닝은 완벽함. I/O나 알고리즘 최적화 필요.

운영 및 아키텍처 도입 체크리스트

  • Redis나 Memcached 같은 인메모리 캐시 서버를 2소켓 이상 머신에 띄울 때, 인스턴스 1개를 크게 띄우지 않고 노드 개수만큼(2개) 쪼개어 각각 노드 0번과 1 번에 묶어(Pinning) 띄우는 수평 확장 스크립트를 짰는가?
  • 클라우드 비용을 아끼기 위해 거대 단일 DB용 EC2를 빌릴 때, NUMA의 존재를 인지하고 PostgreSQL의 NUMA 최적화 파라미터가 켜져 있는지 확인했는가?

안티패턴: 최신 128코어 AMD EPYC(거대한 NUMA 덩어리) 서버를 사놓고, 자바(Java) 옵션에 NUMA Aware GC(-XX:+UseNUMA) 옵션을 빼먹어, 쓰레기 수집기(GC) 스레드가 칩 내부의 모든 노드 메모리를 미친 듯이 원격 횡단하며 서버 전체를 마비시키는 것.

📢 섹션 요약 비유: NUMA 아키텍처는 고가의 날카로운 명검과 같습니다. 칼의 성질(로컬/원격 지연)을 모르는 자가 휘두르면 자기 손(OOM, 버스 병목)을 베이지 만, 결을 따라 부드럽게 썰면(CPU 핀닝, 메모리 분배) 세상 어떤 무거운 워크로드(수백 코어)도 부드럽게 두 동강 내는 최고의 무기입니다.


Ⅴ. 기대효과 및 결론 (Future & Standard)

NUMA 아키텍처는 칩 바깥에서 해결해야 했던 분산 시스템의 확장성을, 메인보드 위 칩 내부 수준으로 끌어들여 엔터프라이즈 하드웨어의 생명줄을 무한히 연장시킨 위대한 타협이다.

척도과거 UMA 고집 시대현대 ccNUMA 아키텍처 적용하드웨어 인프라 기대효과
CPU 장착 한계버스 폭발로 단일 보드 16코어 수준 정체메인보드 1장에 100~200코어 이상 거대 밀집 탑재오라클, SAP HANA 등 거대 단일 In-Memory DB의 구동 스펙 달성
메모리 대역폭(Bandwidth)모든 코어가 1개의 병목 메모리를 다툼각자 로컬 메모리 파이프를 열어 대역폭 N배로 뻥튀기초고해상도 영상 처리 및 클라 우드 하이퍼바이저 통신 병목 분쇄

미래 전망: 2소켓, 4소켓 서버의 전유물이었던 NUMA는 이제 단 하나의 칩(CPU 1개) 안으로 기어들어 오고 있다. AMD의 칩렛(Chiplet) 아키텍처와 인텔의 타일(Tile) 구조는, CPU 1개 속에 수십 개의 작은 다이(Die)를 패키징하며 단일 칩 내부에서조차 거리가 발생하여 NUMA로 작동하게 만들었다(Sub-NUMA Clustering). 미래 에는 CXL 기술이 등장하여 랙(Rack) 전체에 꽂힌 다른 컴퓨터의 메모리마저 아주 먼 NUMA 노드의 하나로 취급해버리는, **소프트웨어 정의 거대 NUMA 풀(Rack-Scale NUMA)**의 시대로 무한 확장할 것이다.

📢 섹션 요약 비유: 과거에는 4명이 한 방에서 밥을 먹는 좁은 UMA 시대였다면, 지금은 수백 명이 각자 방에서 배달음식을 시켜 먹는 NUMA 시대로 바뀌었습니다. 미래에는 옆 동네 아파트의 냉장고마저 우리 집 부엌에 이어진 NUMA 노드 중 하나처럼 편하게 빼먹는 초연결(CXL) 시대로 향하고 있습니다.


📌 관련 개념 맵 (Knowledge Graph)

  • UMA (Uniform Memory Access) | 모든 CPU가 똑같은 시간, 똑같은 버스로 하나의 메모리를 향해 돌진하는 NUMA 이전의 구형/소규모 대칭형 아키텍처
  • ccNUMA (Cache-Coherent NUMA) | 로컬과 원격 메모리로 분리된 복잡한 구조 속에서도 각 코어 캐시의 일관성을 하드웨어가 알아서 맞춰주는 기적의 디렉터리 기술
  • 인터리빙 (Interleaving) | NUMA의 극심한 메모리 불균형(특정 램만 터져나감)을 막기 위해 OS가 짝수 주소는 노드 0에, 홀수 주소는 노드 1에 강제로 찢어발겨 나누는 튜닝 기법
  • OOM Killer (Out Of Memory) | 전체 시스템 메모리는 넉넉한데, First-Touch 정책에 의해 특정 로컬 NUMA 노드의 램만 100% 꽉 차버려 OS가 미친 줄 알고 프로세 스를 강제 사살하는 비극적 현상
  • 스레드 핀닝 (Thread Pinning / Affinity) | 스레드가 이리저리 코어를 옮겨 다니다 원격 메모리 페널티를 맞는 것을 막기 위해, 수갑을 채워 특정 코어(로컬 램)에만 묶어버리는 실무 튜닝 기법

👶 어린이를 위한 3줄 비유 설명

  1. 개념: NUMA는 아주 커다란 회사의 사무실 배치를 바꾼 거예요. 원래는 가운데 큰 복사기 1대를 100명이 같이 썼는데(UMA), 줄이 너무 길어져서 각 부서 방마다 작은 복사기를 하나씩 놔준 방식이죠.
  2. 원리: 내 부서 방에 있는 복사기를 쓰면 1초 만에 끝나지만(로컬 접근), 만약 고장 나서 옆 부서 방 복사기까지 걸어가려면 5초가 걸리는(원격 접근) 불공평한 구조가 되었어요.
  3. 효과: 조금 복잡해지긴 했지만, 내 부서 복사기만 잘 쓰면 기다릴 필요가 전혀 없어졌기 때문에, 컴퓨터는 수백 개의 거대한 두뇌(CPU)를 달고도 멈추지 않 고 미친 듯이 빠르게 일할 수 있게 되었답니다.