[Daily morning study] NUMA (Non-Uniform Memory Access) ์ํคํ ์ฒ
#daily morning study
NUMA๋?
NUMA(Non-Uniform Memory Access)๋ ๋ฉํฐํ๋ก์ธ์ ์์คํ ์์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ์๊ฐ์ด CPU์ ๋ฉ๋ชจ๋ฆฌ ๊ฐ์ ๋ฌผ๋ฆฌ์ ์์น์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ์ํคํ ์ฒ๋ค.
ํ๋ ์๋ฒ์๋ ์์ญ ๊ฐ์ CPU ์ฝ์ด์ ์๋ฐฑ GB์ RAM์ด ํ์ฌ๋์ด ์๋๋ฐ, ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ๋์ ๋ฒ์ค๋ก ์ฐ๊ฒฐํ๋ฉด ๋ณ๋ชฉ์ด ์ฌํด์ง๋ค. NUMA๋ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฉ๋ชจ๋ฆฌ๋ฅผ CPU์ ๊ฐ๊น์ด ๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ์ ์๊ฒฉ ๋ฉ๋ชจ๋ฆฌ๋ก ๋๋ ๊ด๋ฆฌํ๋ค.
UMA vs NUMA
UMA (Uniform Memory Access)
CPU0 -- CPU1 -- CPU2 -- CPU3
|
๊ณต์ ๋ฒ์ค
|
๋จ์ผ ๋ฉ๋ชจ๋ฆฌ ํ
๋ชจ๋ CPU๊ฐ ๋์ผํ ๋ฒ์ค๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ๋ค. CPU ์๊ฐ ๋์ด๋ ์๋ก ๋ฒ์ค ๊ฒฝํฉ์ด ์ฌํด์ ธ ํ์ฅ์ฑ์ด ๋จ์ด์ง๋ค.
NUMA
[NUMA Node 0] [NUMA Node 1]
CPU0 CPU1 CPU2 CPU3
\ / \ /
๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ <-- Interconnect --> ๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ
๊ฐ CPU(๋๋ CPU ๊ทธ๋ฃน)๋ ์์ ์ ๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ง๋ค. ๋ค๋ฅธ ๋ ธ๋์ ๋ฉ๋ชจ๋ฆฌ(์๊ฒฉ ๋ฉ๋ชจ๋ฆฌ)์๋ ์ ๊ทผํ ์ ์์ง๋ง, ์ธํฐ์ปค๋ฅํธ(์: QPI, UPI, Infinity Fabric)๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ฏ๋ก ์ถ๊ฐ ์ง์ฐ์ด ๋ฐ์ํ๋ค.
NUMA ๋ ธ๋(Node)
NUMA์์ ๋ ธ๋๋ CPU์ ๊ทธ CPU์ ์ง์ ์ฐ๊ฒฐ๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ฌถ์์ด๋ค.
| ๊ตฌ๋ถ | ์ค๋ช |
|---|---|
| ๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ | ๊ฐ์ ๋ ธ๋์ ๋ฉ๋ชจ๋ฆฌ โ ์ง์ฐ ๋ฎ์ |
| ์๊ฒฉ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ | ๋ค๋ฅธ ๋ ธ๋์ ๋ฉ๋ชจ๋ฆฌ โ ์ธํฐ์ปค๋ฅํธ ๊ฒฝ์ , ์ง์ฐ ๋์ |
| NUMA ratio | ์๊ฒฉ vs ๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ๋น์ฉ ๋น์จ (๋ณดํต 1.5~3๋ฐฐ) |
๋ฆฌ๋ ์ค์์ NUMA ํ์ธ
# NUMA ํ ํด๋ก์ง ํ์ธ
numactl --hardware
# ์ถ๋ ฅ ์์
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 32768 MB
node 0 free: 28000 MB
node 1 cpus: 8 9 10 11 12 13 14 15
node 1 size: 32768 MB
node 1 free: 27500 MB
node distances:
node 0 1
0: 10 21
1: 21 10
node distances์ ์ซ์๊ฐ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ๋น์ฉ์ ์๋์ ์ง์ฐ์ด๋ค. ๊ฐ์ ๋
ธ๋(10)๋ณด๋ค ์๊ฒฉ ๋
ธ๋(21)๊ฐ ์ฝ 2๋ฐฐ ๋๋ฆฌ๋ค.
NUMA ๋ฌธ์ : Remote Memory Access
ํ๋ก์ธ์ค๋ ์ค๋ ๋๊ฐ ์์ ์ด ์คํ ์ค์ธ CPU ๋ ธ๋๊ฐ ์๋ ๋ค๋ฅธ ๋ ธ๋์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์ฃผ ์ ๊ทผํ๋ฉด ์ฑ๋ฅ์ด ํฌ๊ฒ ์ ํ๋๋ค.
ํ๋ก์ธ์ค๊ฐ Node 0์ CPU์์ ์คํ ์ค์ธ๋ฐ
ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ Node 1์ ์๋ค๋ฉด?
โ ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ด ์๊ฒฉ ์ ๊ทผ = ์ฑ๋ฅ ์ ํ
์ด๋ฅผ NUMA miss ๋๋ NUMA imbalance๋ผ ๋ถ๋ฅธ๋ค.
๋ฆฌ๋ ์ค NUMA ์ ์ฑ
๋ฆฌ๋ ์ค ์ปค๋์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ NUMA ์ ์ฑ ์ ํตํด ์ด๋ ๋ ธ๋์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ฌ์ง ๊ฒฐ์ ํ๋ค.
| ์ ์ฑ | ์ค๋ช |
|---|---|
default | ํ์ฌ ์คํ ์ค์ธ CPU์ ๋ก์ปฌ ๋ ธ๋์์ ์ฐ์ ํ ๋น |
bind | ํน์ ๋ ธ๋์์๋ง ํ ๋น, ์์ผ๋ฉด OOM |
preferred | ํน์ ๋ ธ๋ ์ ํธ, ์์ผ๋ฉด ๋ค๋ฅธ ๋ ธ๋์์ ํ ๋น |
interleave | ์ฌ๋ฌ ๋ ธ๋์ ๋ผ์ด๋๋ก๋น ๋ฐฉ์์ผ๋ก ๋ถ์ฐ ํ ๋น |
numactl ๋ช
๋ น์ผ๋ก ํน์ ์ ์ฑ
์ ์ง์ ํด ํ๋ก์ธ์ค๋ฅผ ์คํํ ์ ์๋ค.
# Node 0์ CPU์ ๋ฉ๋ชจ๋ฆฌ๋ง ์ฌ์ฉ
numactl --cpunodebind=0 --membind=0 ./my_app
# ๋ ๋
ธ๋์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ถ์ฐ
numactl --interleave=0,1 ./my_app
NUMA-aware ๋ฉ๋ชจ๋ฆฌ ํ ๋น
C์์ NUMA ์นํ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ํ์ํ๋ฉด libnuma๋ฅผ ์ฌ์ฉํ๋ค.
#include <numa.h>
// Node 0์ 1MB ๋ฉ๋ชจ๋ฆฌ ํ ๋น
void *ptr = numa_alloc_onnode(1024 * 1024, 0);
// ํน์ ๋
ธ๋ ์ ํธ ์ค์
numa_set_preferred(0);
// ํ ๋น ํด์
numa_free(ptr, 1024 * 1024);
NUMA Balancing (์๋ ๋ง์ด๊ทธ๋ ์ด์ )
๋ฆฌ๋ ์ค ์ปค๋์ NUMA Balancing ๊ธฐ๋ฅ์ ํตํด ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด์ ๋ถ์ํ๊ณ , ์๊ฒฉ ์ ๊ทผ์ด ๋ง์ ํ์ด์ง๋ฅผ ํด๋น CPU์ ๋ก์ปฌ ๋ ธ๋๋ก ์ด๋์ํจ๋ค.
# NUMA Balancing ํ์ฑํ ์ฌ๋ถ ํ์ธ
cat /proc/sys/kernel/numa_balancing
# 1์ด๋ฉด ํ์ฑํ
# ๋นํ์ฑํ
echo 0 > /proc/sys/kernel/numa_balancing
๋จ, ํ์ด์ง ๋ง์ด๊ทธ๋ ์ด์ ์ ๋น์ฉ์ด ์์ผ๋ฏ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ค๋ฒํค๋๊ฐ ์ ๊ทผ ์ง์ฐ ์ ๊ฐ๋ณด๋ค ํฌ๋ฉด ์คํ๋ ค ์ฑ๋ฅ์ด ๋๋น ์ง ์ ์๋ค.
NUMA ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
# NUMA ํต๊ณ ํ์ธ
numastat
# ์ถ๋ ฅ ์์
node0 node1
numa_hit 50000000 45000000 # ๋ก์ปฌ ๋
ธ๋์์ ์ฑ๊ณต์ ํ ๋น
numa_miss 2000000 3000000 # ์๊ฒฉ ๋
ธ๋์์ ํ ๋น๋ ํ์
numa_foreign 3000000 2000000 # ๋ค๋ฅธ ๋
ธ๋๋ก ๋์ด๊ฐ ํ ๋น
interleave_hit 100000 100000
local_node 49000000 44000000
other_node 3000000 4000000
numa_miss๊ฐ ๋๋ค๋ฉด NUMA ๋ถ๊ท ํ์ด ์๋ค๋ ์ ํธ๋ค.
์ค๋ฌด์์์ NUMA ์ต์ ํ ์ ๋ต
1. CPU์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ ๋ ธ๋์ ๋ฐ์ธ๋ฉ
๋ฐ์ดํฐ๋ฒ ์ด์ค, ์บ์ ์๋ฒ์ฒ๋ผ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฐ๋ ํ๋ก์ธ์ค๋ ํน์ ๋ ธ๋์ ๊ณ ์ ํด ๋ก์ปฌ ์ ๊ทผ์ ๊ทน๋ํํ๋ค.
numactl --cpunodebind=0 --membind=0 redis-server
2. ๋ฉ๋ชจ๋ฆฌ ์ธํฐ๋ฆฌ๋น
๋ค์์ ์ค๋ ๋๊ฐ ๊ณ ๋ฅด๊ฒ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ๊ทผํ๋ ์ํฌ๋ก๋(์: ๋๊ท๋ชจ ํ๋ ฌ ์ฐ์ฐ)๋ ์ธํฐ๋ฆฌ๋น์ผ๋ก ๋์ญํญ์ ๊ณ ๋ฅด๊ฒ ํ์ฉํ๋ค.
numactl --interleave=all ./matrix_multiply
3. JVM ์ ํ๋ฆฌ์ผ์ด์
JVM์ ๊ธฐ๋ณธ์ ์ผ๋ก NUMA๋ฅผ ์ธ์ํ์ง ๋ชปํ๋ฏ๋ก -XX:+UseNUMA ์ต์
์ผ๋ก NUMA ์นํ์ ํ ํ ๋น์ ํ์ฑํํ๋ค.
java -XX:+UseNUMA -XX:+UseParallelGC -jar app.jar
4. Docker/Kubernetes์์์ NUMA
์ปจํ
์ด๋ ํ๊ฒฝ์์๋ cpuset cgroup์ ํตํด ํน์ ๋
ธ๋์ CPU์ ๋ฉ๋ชจ๋ฆฌ๋ง ์ฌ์ฉํ๋๋ก ๊ฒฉ๋ฆฌํ ์ ์๋ค. Kubernetes์ Topology Manager๊ฐ ์ด๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ค.
NUMA์ False Sharing
๋ฉํฐ์ฝ์ด ํ๊ฒฝ์์๋ NUMA ์ธ์๋ False Sharing ๋ฌธ์ ๋ฅผ ํจ๊ป ๊ณ ๋ คํด์ผ ํ๋ค. ์๋ก ๋ค๋ฅธ CPU ์ฝ์ด๊ฐ ๊ฐ์ ์บ์ ๋ผ์ธ์ ์๋ ์๋ก ๋ค๋ฅธ ๋ณ์๋ฅผ ์์ ํ๋ฉด ๋ถํ์ํ ์บ์ ๋ฌดํจํ๊ฐ ๋ฐ์ํ๋ค. NUMA ํ ํด๋ก์ง๋ฅผ ์ ์ค๊ณํ๋๋ผ๋ False Sharing์ด ์์ผ๋ฉด ์ฑ๋ฅ ์ด์ ์ด ์์๋๋ค.
์์ฝ
| ๊ฐ๋ | ํต์ฌ ๋ด์ฉ |
|---|---|
| NUMA | ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ์๊ฐ์ด CPU-๋ฉ๋ชจ๋ฆฌ ๋ฌผ๋ฆฌ์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ์ํคํ ์ฒ |
| ๋ก์ปฌ vs ์๊ฒฉ ์ ๊ทผ | ๋ก์ปฌ์ ๋น ๋ฅด๊ณ , ์๊ฒฉ์ ์ธํฐ์ปค๋ฅํธ๋ฅผ ๊ฑฐ์ณ ๋๋ฆผ |
| NUMA ์ ์ฑ | default / bind / preferred / interleave |
| NUMA Balancing | ์ปค๋์ด ์๊ฒฉ ์ ๊ทผ ํ์ด์ง๋ฅผ ์๋์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ |
| ์ต์ ํ ๋ฐฉํฅ | CPUยท๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ ๋ ธ๋์ ๋ฐ์ธ๋ฉํ๊ฑฐ๋ ์ธํฐ๋ฆฌ๋น์ผ๋ก ๋์ญํญ ๋ถ์ฐ |
์๋ฒ ์ฑ๋ฅ ํ๋ ์ NUMA ํ ํด๋ก์ง๋ฅผ ์ดํดํ๊ณ ์์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด์ ์ต์ ํํ์ฌ ์์์น ๋ชปํ ์ง์ฐ์ ์ค์ผ ์ ์๋ค.