코어 수와 데이터 접근 거리
NUMA 최적화의 단위는 코어 수가 아니라 작업이 반복해서 읽고 쓰는 페이지와 그 페이지를 소유한 노드입니다. 스케줄러가 작업을 어느 코어에 배치했는지와 OS가 페이지를 어느 노드에 두었는지는 별도 상태라서 함께 관찰해야 합니다.
NUMA 서버는 CPU와 메모리가 여러 노드로 나뉘어 접근 비용이 균일하지 않습니다. 한 노드의 CPU가 다른 노드의 페이지를 읽으면 원격 링크·메모리 대역폭 비용이 추가될 수 있습니다. 스레드를 다른 노드까지 늘렸는데 데이터는 한곳에 남으면 처리량이 기대만큼 늘지 않을 수 있습니다.
코어 수뿐 아니라 누가 페이지를 준비하고 누가 반복해서 사용하는가를 함께 봐야 합니다. 작은 데이터가 cache에 들어가는 벤치마크는 실제 원격 메모리 비용을 숨길 수 있습니다.
First-touch 배치 결과 추적
first-touch 정책이 적용되는 환경에서 초기화 스레드 하나가 큰 배열의 모든 페이지를 처음 쓰면 그 노드 쪽에 페이지가 몰릴 수 있습니다. 이후 두 노드의 worker가 절반씩 읽어도 다른 노드 worker는 원격 페이지를 읽습니다.
그림을 글로 읽기
- 노드 0 worker
- 노드 1 worker
- 노드 0의 페이지: 노드 0 초기화 스레드가 first-touch
- 노드 0 worker → 노드 0의 페이지: 로컬 접근
- 노드 1 worker → 노드 0의 페이지: 원격 접근
각 worker가 앞으로 사용할 구간을 자기 노드에서 초기화하면 지역성을 높일 수 있습니다. 다만 실제 페이지 배치는 자동 NUMA balancing·메모리 압박·정책에 따라 바뀔 수 있으므로 초기화 코드만으로 배치를 단정하지 않습니다.
작업 이동과 페이지 이동 비용
노드 0에서 준비한 작업을 노드 1의 유휴 코어로 옮기면 run queue 대기는 줄어도 warm cache를 잃고 노드 0의 페이지를 원격으로 읽을 수 있습니다. 반대로 페이지를 노드 1로 옮기면 이후 접근의 지역성은 좋아질 수 있지만 페이지 이동·매핑 변경·무효화 비용을 먼저 지불합니다. 작업의 남은 계산량과 데이터 크기·재사용 기간이 짧은 burst라면 어느 이동도 이득보다 클 수 있으므로, 유휴 코어라는 한 가지 지표만으로 결정하지 않습니다.
| 선택 | 이득 | 대가 |
|---|---|---|
| 강한 CPU affinity | 이동·cache 손실 감소 | 한 노드 과부하와 다른 노드 유휴 |
| local-first work stealing | 평소 지역성, 필요 시 부하 분산 | 훔친 작업의 원격 데이터 접근 |
| 데이터 복제 | 읽기 지역성 | 메모리·갱신 일관성 |
| 페이지 migration | 장기 접근 위치 개선 | 이동·TLB·일시 지연 |
작업의 남은 계산량·데이터 크기·재사용 기간을 근거로 결정합니다. 다른 코어가 놀고 있다는 사실만으로 이동이 항상 이득은 아닙니다.
실무 선택은 작은 작업의 즉시 균형보다 긴 재사용 구간의 locality를 우선할지로 나눕니다. 짧은 burst는 remote read를 감수하고 stealing하는 편이 나을 수 있고, 큰 working set을 오래 처리하는 작업은 affinity·데이터 분할·복제를 함께 검토할 수 있습니다. 결과는 처리량뿐 아니라 p99와 노드별 포화도까지 비교합니다.
노드별 풀과 객체 반환 경로
노드 0 풀에서 만든 객체를 노드 1이 오래 사용하면 원격 접근이 늘 수 있습니다. 사용 노드에 복사할지 원래 객체를 그대로 읽을지, 해제는 원래 풀로 보내거나 공용 경로로 처리할지 정해야 합니다.
반환을 원래 노드 큐로 보내면 직접 remote free 경합을 줄일 수 있지만 반환 큐·지연 보유 메모리가 생깁니다. 객체의 참조가 남은 동안 풀을 바꾸거나 재사용하면 안 됩니다. 물리 페이지 지역성과 논리 소유권의 정확성은 다른 조건입니다.
원격 메모리와 거짓 공유의 구분
서로 다른 카운터가 같은 cache line에 있고 여러 코어가 쓰면 라인 소유권이 반복 이동할 수 있습니다. 이는 같은 페이지가 원격 노드에 있다는 문제와 다릅니다. 둘이 동시에 생길 수도 있으므로 cache line 배치·쓰기 빈도·원격 페이지 접근을 별도로 측정해야 합니다.
읽기 전용 공유와 여러 writer의 공유도 비용이 다릅니다. padding은 거짓 공유를 줄일 수 있지만 전체 메모리와 cache footprint를 늘립니다. 노드별 합산 후 병합은 쓰기 공유를 줄이는 대신 결과 최신성·병합 비용을 추가합니다.
동일한 실제 작업 집합의 NUMA 배치 비교
큰 배열을 한 스레드가 초기화한 경우와 worker별 구간 초기화를 비교합니다. CPU affinity만 변경한 경우, 데이터 분할만 변경한 경우, 둘 다 바꾼 경우를 구분합니다. 노드별 메모리·대역폭·remote access·migration·run queue·p99를 기록합니다.
객체 전달 크기·횟수·수명과 반환 지연도 관찰하고, 정상 균등 부하뿐 아니라 한 노드 집중과 장애 재배치에서 전체 처리량을 확인합니다. 원격 접근 지표 하나가 줄었다고 사용자 경험이 개선됐다고 단정하지 않습니다.
예상 결과는 한 스레드 초기화 후 두 노드가 절반씩 읽을 때 노드 1의 remote access가 증가하고, worker별 first-touch가 이를 줄일 가능성이 있는 것입니다. 그러나 한 노드로 affinity를 고정하면 remote access는 줄어도 run queue와 p99가 악화될 수 있으므로 두 지표를 함께 판정합니다.