[Daily morning study] NUMA (Non-Uniform Memory Access) ์•„ํ‚คํ…์ฒ˜

#daily morning study

Image


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 ํ† ํด๋กœ์ง€๋ฅผ ์ดํ•ดํ•˜๊ณ  ์žˆ์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ํŒจํ„ด์„ ์ตœ์ ํ™”ํ•˜์—ฌ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ง€์—ฐ์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.