| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- low latency
- DMA trading
- 속도 차익거래
- dma hft
- 시스템 트레이딩
- dma거래
- algo trading
- Korea krx
- KRX HFT
- DMA개발
- mmlp
- krx nxt
- korea inbound
- 저지연시스템
- dma development
- hft개발
- 자동매매
- dma fep
- 프랍데스크
- HFT system
- 트레이딩 엔지니어링
- Arbitrage Trading
- low latency trading
- high frequency trading
- 주문fep
- ultra low latency
- trading engineering
- system trading
- rms개발
- fep개발
- Today
- Total
Fontes Fintech
저지연 트레이딩 시스템에서 NUMA와 Memory Locality가 중요한 이유 | Trading Engineering — Deep Dive #4 본문
저지연 트레이딩 시스템에서 NUMA와 Memory Locality가 중요한 이유 | Trading Engineering — Deep Dive #4
폰테스 핀테크 :: 금융거래의 안전한 미래 2026. 9. 22. 20:58
저지연 트레이딩 시스템에서 NUMA와 Memory Locality가 중요한 이유
Trading Engineering — Deep Dive #4
저지연 트레이딩 시스템에서는 CPU의 성능만큼 메모리가 어디에 위치하는가도 중요합니다.
특히 여러 CPU 소켓과 NUMA(Non-Uniform Memory Access) 구조를 사용하는 서버에서는 모든 메모리 접근이 동일한 비용을 가지지 않습니다.
같은 서버 안에서도
어떤 CPU가 어떤 메모리에 접근하는가
에 따라 메모리 접근 시간과 시스템의 latency가 달라질 수 있습니다.
1. NUMA란 무엇인가?
NUMA는 Non-Uniform Memory Access의 약자입니다.
말 그대로 메모리 접근 비용이 균일하지 않은 구조를 의미합니다.
전통적인 단일 CPU 시스템에서는 다음과 같이 생각하기 쉽습니다.
CPU
↓
Memory
CPU에서 메모리에 접근하면 항상 비슷한 비용이 발생한다고 생각할 수 있습니다.
하지만 여러 CPU 소켓을 사용하는 서버에서는 구조가 달라집니다.
CPU 0 CPU 1
│ │
Local Memory Local Memory
│ │
└────────── Interconnect ───┘
CPU 0에 가까운 메모리는 CPU 0에서 접근할 때 상대적으로 Local Memory가 됩니다.
반면 CPU 0이 CPU 1에 연결된 메모리에 접근하면 Remote Memory Access가 됩니다.
즉,
CPU 0 → Memory 0 Local Access
CPU 0 → Memory 1 Remote Access
처럼 접근 경로가 달라질 수 있습니다.
2. 왜 Memory Locality가 중요한가?
저지연 시스템에서는 단순히 CPU 연산량만 줄이는 것으로 충분하지 않습니다.
CPU가 데이터를 필요로 할 때
CPU
↓
Cache
↓
Memory
라는 데이터 접근 경로가 발생합니다.
이때 데이터가 CPU와 가까운 위치에 있고 cache에 잘 유지된다면 효율적으로 처리할 수 있습니다.
반대로 데이터가 다른 NUMA 노드의 메모리에 있다면 CPU가 다른 노드까지 접근해야 할 수 있습니다.
개념적으로 보면:
Local Access
CPU 0
│
↓
Memory 0
Remote Access
CPU 0
│
↓
Interconnect
│
↓
Memory 1
저지연 시스템에서는 이러한 차이가 누적될 수 있습니다.
특히 매우 높은 메시지 처리량이나 빈번한 메모리 접근이 발생하는 시스템에서는 Memory Locality가 중요한 설계 요소가 됩니다.
3. CPU Affinity와 NUMA는 함께 봐야 한다
앞선 Deep Dive #3에서는 CPU Affinity와 Core Pinning을 살펴봤습니다.
그런데 CPU를 특정 Core에 고정하는 것만으로 충분할까요?
그렇지 않습니다.
예를 들어:
CPU Core 0
│
└── NUMA Node 0
│
Memory 0
라고 할 때 Core 0에서 실행되는 스레드가 Node 1의 메모리를 계속 사용한다면 CPU만 잘 고정했다고 해서 최적의 locality가 만들어지는 것은 아닙니다.
따라서 저지연 시스템에서는 다음을 함께 고려해야 합니다.
Thread
↓
CPU Core
↓
NUMA Node
↓
Memory
이 네 가지의 관계가 중요합니다.
4. CPU와 Memory의 위치를 생각하라
멀티소켓 서버에서는 다음과 같은 구조를 생각할 수 있습니다.
NUMA Node 0 NUMA Node 1
┌─────────────┐ ┌─────────────┐
│ CPU 0 │ │ CPU 1 │
│ Core 0~N │ │ Core 0~N │
└──────┬──────┘ └──────┬──────┘
│ │
Memory 0 Memory 1
│ │
└────── Interconnect ────┘
이 구조에서 Node 0에서 주로 실행되는 애플리케이션이 Node 0의 메모리를 사용하는 것과 Node 1의 메모리를 사용하는 것은 동일하지 않을 수 있습니다.
따라서 시스템 설계 단계에서부터 CPU와 데이터의 위치 관계를 생각해야 합니다.
5. Trading System에서는 어떻게 적용할까?
예를 들어 다음과 같은 구조의 시스템을 생각해보겠습니다.
Market Data
↓
Order Book
↓
Strategy
↓
Risk Check
↓
Order Manager
↓
DMA / FEP
이러한 각 단계가 서로 다른 CPU와 NUMA Node에 흩어져 있다면 데이터가 CPU 사이를 이동하는 과정에서 추가적인 비용이 발생할 수 있습니다.
반대로 특정 처리 흐름을 하나의 NUMA 영역 안에서 최대한 유지하면 데이터 locality를 개선할 수 있습니다.
예를 들어:
NUMA Node 0
Market Data
↓
Order Book
↓
Strategy
↓
Risk
↓
Order Manager
그리고 다른 Node에서는
NUMA Node 1
Monitoring
Logging
Administration
Other Services
와 같이 역할을 분리할 수도 있습니다.
물론 실제 구성은 시스템의 workload와 CPU topology에 따라 달라집니다.
6. NIC까지 함께 생각해야 한다
금융 트레이딩 시스템에서는 NUMA를 CPU와 메모리만의 문제로 보면 부족합니다.
네트워크 카드(NIC)의 위치도 중요합니다.
예를 들어:
NUMA Node 0
┌──────────────────┐
│ CPU │
│ Memory │
│ │
│ NIC │
└──────────────────┘
와 같이 NIC와 CPU가 같은 NUMA 영역에 가까이 배치되어 있다면 데이터가 이동하는 경로를 단순화할 수 있습니다.
반대로:
CPU 0
│
↓
NUMA 0
│
Interconnect
│
↓
NUMA 1
│
NIC
처럼 네트워크 데이터가 다른 NUMA 노드를 거쳐야 하는 구조라면 추가적인 데이터 이동 비용이 발생할 수 있습니다.
따라서 고성능 네트워크를 사용하는 시스템에서는
CPU ↔ Memory ↔ NIC
의 물리적 topology를 함께 확인해야 합니다.
7. Memory Allocation도 중요하다
NUMA 시스템에서는 단순히 malloc()을 호출하는 것만으로 메모리 위치를 개발자가 명확하게 통제하고 있다고 생각해서는 안 됩니다.
운영체제와 메모리 정책에 따라 실제 물리적인 메모리 배치는 달라질 수 있습니다.
Linux에서는 NUMA 정책을 확인하고 제어하기 위해 다양한 기능을 사용할 수 있습니다.
대표적으로:
numactl
libnuma
등을 사용할 수 있습니다.
예를 들어 특정 NUMA 노드에서 프로세스를 실행하도록 설정할 수 있습니다.
numactl --cpunodebind=0 --membind=0 ./trading_app
이 명령은 개념적으로 CPU와 메모리를 NUMA Node 0에 묶어 실행하도록 요청합니다.
다만 실제 운영환경에서는 시스템의 전체 CPU topology와 다른 프로세스의 자원 사용까지 함께 고려해야 합니다.
8. First-Touch와 Memory Placement
NUMA 시스템에서는 메모리 할당 시점뿐 아니라 실제로 메모리에 접근하는 방식도 중요합니다.
특히 Linux의 NUMA 환경에서는 흔히 First-Touch라는 개념을 접하게 됩니다.
간단히 말하면:
Memory Allocation
↓
Physical Page가 실제로 배치됨
↓
처음 접근한 CPU / NUMA Node의 영향을 받을 수 있음
따라서 다음과 같은 코드가 있다고 해보겠습니다.
buffer = malloc(size);
/* CPU 0 */
initialize(buffer);
그 이후 CPU 1에서 이 buffer를 매우 빈번하게 사용한다면, 애플리케이션이 의도한 locality와 실제 memory placement가 달라질 수 있습니다.
대규모 메모리 영역을 사용하는 시스템에서는 이런 차이가 중요해질 수 있습니다.
9. Shared Memory도 NUMA의 영향을 받는다
금융 시스템에서는 프로세스 간 통신을 위해 Shared Memory를 자주 사용할 수 있습니다.
예를 들어:
Market Data Process
│
↓
Shared Memory
│
↓
Strategy Process
Shared Memory 자체가 빠르더라도 CPU와 메모리의 topology가 좋지 않으면 기대했던 성능이 나오지 않을 수 있습니다.
특히 여러 프로세스가 서로 다른 NUMA Node에서 동일한 데이터를 반복적으로 읽고 쓰는 경우에는 데이터 이동과 cache coherence traffic을 함께 고려해야 합니다.
따라서
Shared Memory = 항상 빠르다
라고 단순하게 생각해서는 안 됩니다.
정확하게는
Shared Memory + 적절한 CPU/Memory Locality
가 중요합니다.
10. NUMA와 Cache는 연결되어 있다
앞선 Deep Dive #1에서 CPU Cache를 살펴봤습니다.
NUMA 역시 Cache와 분리해서 생각하기 어렵습니다.
데이터 접근 경로를 단순화하면:
Thread
↓
CPU Core
↓
L1 / L2 Cache
↓
L3 Cache
↓
Local Memory
↓
Remote Memory
일반적으로 아래쪽으로 갈수록 데이터 접근 비용과 경로가 커질 가능성이 있습니다.
따라서 저지연 시스템의 성능을 개선할 때는 단순히
CPU 속도를 높인다
가 아니라
Thread Placement
↓
CPU Core
↓
Cache Locality
↓
Memory Locality
↓
NUMA Locality
↓
Network Locality
라는 전체 경로를 봐야 합니다.
11. NUMA에서 흔히 발생하는 문제
① Remote Memory Access
CPU가 다른 NUMA Node의 메모리를 자주 접근하는 경우입니다.
② 잘못된 CPU Placement
스레드는 Node 0에서 실행되는데 데이터는 Node 1에 집중되어 있는 경우입니다.
③ 데이터 공유
여러 NUMA Node의 스레드가 동일한 데이터를 빈번하게 변경하면 cache coherence와 memory traffic이 증가할 수 있습니다.
④ 과도한 Cross-NUMA Communication
처리 단계가 여러 NUMA Node에 분산되어 데이터가 계속 Node 사이를 이동하는 경우입니다.
12. 어떻게 측정할 것인가?
NUMA 최적화 역시 추측으로 접근해서는 안 됩니다.
먼저 서버의 topology를 확인해야 합니다.
Linux에서는 다음과 같은 도구를 사용할 수 있습니다.
lscpu
numactl --hardware
numastat
그리고 애플리케이션 성능 측면에서는 다음과 같은 지표를 함께 볼 수 있습니다.
- CPU utilization
- CPU migration
- Cache miss
- Memory bandwidth
- Memory latency
- NUMA hit / miss
- Remote memory access
- Context switch
- IPC
- P50 / P95 / P99 / P99.9 latency
- Throughput
특히 저지연 트레이딩 시스템에서는 평균 latency만 보는 것보다 tail latency가 어떻게 변화하는지를 확인하는 것이 중요합니다.
13. NUMA 최적화의 기본 원칙
모든 시스템을 복잡하게 NUMA-aware 구조로 만들 필요는 없습니다.
하지만 고성능 시스템에서는 다음과 같은 원칙을 고려할 수 있습니다.
1. CPU와 데이터를 가깝게 배치한다
Thread
↓
CPU
↓
Local Memory
2. 불필요한 Cross-NUMA 데이터를 줄인다
가능하다면 하나의 처리 흐름이 여러 NUMA Node를 반복해서 넘나들지 않도록 합니다.
3. 데이터 Ownership을 명확하게 한다
각 스레드 또는 NUMA Node가 자신이 주로 사용하는 데이터를 관리하도록 설계합니다.
4. Shared Data를 최소화한다
특히 빈번하게 변경되는 데이터의 무분별한 공유는 피합니다.
5. NIC topology를 확인한다
네트워크 데이터가 어느 NUMA Node로 들어오는지 확인합니다.
6. Benchmark로 검증한다
NUMA 최적화 전후의 latency와 throughput을 실제 workload에서 비교합니다.
14. CPU, Memory, NIC를 하나의 시스템으로 보자
저지연 트레이딩 시스템에서는 다음 세 가지를 따로 보는 것보다 하나의 데이터 경로로 보는 것이 중요합니다.
CPU
│
↓
Cache
│
↓
Memory
│
↓
NIC
│
↓
Network
실제로는 이보다 훨씬 복잡하지만 핵심적인 질문은 간단합니다.
데이터가 시스템 안에서 어디에서 어디로 이동하는가?
그리고 그 과정에서
- CPU Core가 바뀌는가?
- Cache locality가 깨지는가?
- NUMA Node가 바뀌는가?
- Memory access가 remote가 되는가?
- NIC와 CPU가 멀리 떨어져 있는가?
- 불필요한 데이터 복사가 발생하는가?
를 확인해야 합니다.
15. FontesFintech Engineering Approach
저지연 시스템의 성능은 특정 기술 하나로 결정되지 않습니다.
CPU Affinity를 적용했다고 해서 자동으로 빨라지는 것도 아니고, Shared Memory를 사용한다고 해서 항상 최적의 성능이 나오는 것도 아닙니다.
중요한 것은 데이터가 이동하는 전체 경로를 이해하는 것입니다.
Network
↓
NIC
↓
CPU Core
↓
Cache
↓
Memory
↓
Application
그리고 멀티소켓 시스템에서는 여기에 NUMA topology가 추가됩니다.
따라서 저지연 시스템을 설계할 때는
Thread Placement
→ CPU Locality
→ Cache Locality
→ Memory Locality
→ NUMA Locality
→ Network Locality
를 하나의 연속된 경로로 바라볼 필요가 있습니다.
Conclusion
NUMA는 단순히 서버의 하드웨어 구조에 관한 이야기가 아닙니다.
저지연 트레이딩 시스템에서는 CPU가 데이터를 어디에서 가져오는가가 latency와 throughput, 그리고 latency predictability에 영향을 줄 수 있습니다.
특히 CPU Affinity와 Core Pinning을 적용했다면 다음 단계로 Memory Locality와 NUMA topology까지 함께 살펴보는 것이 자연스럽습니다.
결국 중요한 것은 CPU의 성능 자체가 아니라,
Right Thread. Right Core. Right Memory.
입니다.
그리고 네트워크까지 포함하면,
Right Thread. Right Core. Right Memory. Right NIC.
라는 하나의 원칙으로 확장할 수 있습니다.
Next: Trading Engineering — Deep Dive #5
epoll vs Busy Polling in Low-Latency Trading Systems
다음 글에서는 Linux 네트워크 프로그래밍에서 오랫동안 논쟁이 되는 주제인 epoll과 Busy Polling을 살펴봅니다.
특히 일반적인 서버 프로그램과 저지연 트레이딩 시스템에서 왜 네트워크 이벤트 처리 방식이 달라질 수 있는지 살펴보겠습니다.
