멀티프로세서 스케줄링과 CPU 친화성, NUMA 최적화
멀티프로세서 환경에서 로드 밸런싱, CPU 친화성, NUMA 메모리 배치를 조합해 캐시 효율과 처리량을 관리하는 방법을 정리한다.
2026-08-14 · 최초 발행 2026-01-16
코어 수만 늘려서는 성능이 고르게 나오지 않는다
여러 CPU 코어가 있는 시스템에서 운영체제는 실행할 작업을 나누는 동시에, 어느 코어에서 계속 실행할지와 어느 메모리에 접근할지를 결정한다. 부하를 분산하는 로드 밸런싱, 캐시 재사용을 노리는 코어 친화성, 메모리 위치를 다루는 NUMA 최적화가 함께 작동하는 이유다.
멀티프로세서 환경의 코어는 독립적인 실행 컨텍스트를 가지지만, 메모리와 캐시 계층은 공유한다. 따라서 동시성 제어와 동기화가 필요하며, 스케줄러는 코어 사용률의 균형, 캐시 적중률, 메모리 접근 지연, 응답 시간, 전체 처리량을 함께 고려한다.
작업 이동에는 이득만큼의 비용이 따른다
로드 밸런싱은 특정 코어에 실행 대기 작업이 몰리는 상태를 완화한다. 과부하 코어에서 유휴 코어로 작업을 옮기는 Push Migration, 유휴 코어가 과부하 코어의 작업을 가져오는 Pull Migration, 빈 작업 큐를 가진 코어가 다른 큐의 작업을 가져오는 Work Stealing이 대표적인 방식이다.
판단에는 CPU 사용률, 런큐 길이, 프로세스 우선순위, 실행 시간을 쓸 수 있다. 다만 마이그레이션은 캐시 무효화와 컨텍스트 스위칭 오버헤드를 만든다. 밸런싱을 지나치게 자주 하면 이 비용이 커지고, 반대로 너무 드물면 불균형이 오래 지속된다. CPU 바운드 작업과 I/O 바운드 작업도 같은 방식으로 다룰 수 없다.
CPU 친화성은 캐시를 지키는 선택이다
CPU 친화성은 프로세스나 스레드를 특정 코어에서 실행하도록 제한하거나, 가능하면 같은 코어에 남도록 유도하는 방식이다. 동일 코어에서 계속 실행되면 캐시 지역성이 좋아지고, 프로세스 간 간섭을 줄이며 예측 가능한 성능을 얻을 수 있다.
하드 친화성은 프로세스를 지정한 코어에서만 실행하게 한다. Linux에서는 sched_setaffinity() 시스템 콜로 설정할 수 있다. 소프트 친화성은 동일 코어 실행을 우선하되 필요하면 다른 코어로 이동시키는 방식이며, 대부분의 운영체제가 기본으로 사용한다.
// Linux에서 코어 친화성 설정
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset); // Core 0에 고정
CPU_SET(1, &cpuset); // Core 1에 고정
sched_setaffinity(pid, sizeof(cpuset), &cpuset);
친화성은 캐시 적중률을 높이고 실시간 작업의 예측 가능성을 높이며, NUMA 환경의 메모리 접근 지연을 줄이는 데도 쓸 수 있다. 반면 로드 밸런싱과 충돌할 수 있고, 코어 활용률의 불균형이나 시스템 확장성 제한으로 이어질 수 있다.
NUMA에서는 CPU와 메모리의 거리가 달라진다
NUMA(Non-Uniform Memory Access)에서는 각 프로세서가 로컬 메모리와 원격 메모리에 접근한다. 로컬 메모리 접근이 원격 메모리 접근보다 빠르므로, 특히 멀티소켓 서버에서는 프로세스 배치만이 아니라 메모리 할당 위치도 성능에 영향을 준다.
로컬 할당은 실행 중인 노드의 로컬 메모리를 우선 할당해 메모리 접근 지연을 줄이며, 대부분의 운영체제가 기본 정책으로 채택한다. 인터리빙은 메모리를 여러 NUMA 노드에 균등하게 배치해 메모리 대역폭을 높이는 방식으로, 메모리 집약적 워크로드에 유리하다. 선호 노드 정책은 특정 노드를 우선하되 필요하면 다른 노드도 사용해 유연성과 성능을 조절한다.
# numactl을 통한 NUMA 정책 설정
numactl --cpunodebind=0 --membind=0 ./my_app # Node 0에 바인딩
numactl --interleave=all ./my_app # 인터리빙 모드
프로세스를 NUMA 노드에 고정하고 로컬 노드에 메모리를 우선 할당하는 것이 기본 접근이다. 페이지 마이그레이션으로 동적으로 조정하거나, 스레드 배치를 NUMA 토폴로지에 맞춰 조절할 수도 있다.
# NUMA 통계 확인
numastat
# 프로세스별 NUMA 메모리 사용 확인
numastat -p <pid>
# NUMA 노드 정보 확인
lscpu | grep NUMA
Linux 스케줄러가 코어 간 균형을 다루는 방식
Linux의 CFS(Completely Fair Scheduler)는 각 프로세스에 공평한 CPU 시간을 할당하고, 레드-블랙 트리를 사용해 스케줄링한다. 멀티코어 환경에서는 로드 밸런싱도 지원한다.
스케줄링 도메인은 SMT(Simultaneous Multithreading), MC(Multi-Core), NUMA 도메인으로 계층화된다. 코어마다 독립적인 런큐를 두고 우선순위별 작업을 관리하며, 로드 밸런싱이 필요할 때 런큐 사이에서 작업을 이동시킨다.
워크로드에 맞춰 조합하는 최적화
캐시를 재사용하려면 프로세스를 동일 코어에 유지하고, 데이터 배치에서는 캐시 라인 크기와 False Sharing을 고려한다. NUMA 환경에서는 노드별 메모리 할당, 페이지 마이그레이션, 로컬 메모리 우선 접근을 함께 검토한다.
병렬 작업은 모든 코어를 활용할 수 있도록 나누되 동기화 오버헤드를 줄여야 한다. Lock-Free 자료구조도 선택지가 된다. 실시간 성능이 필요한 경우에는 실시간 프로세스를 특정 코어에 고정하고 일반 작업과 분리하며, 인터럽트 친화성도 설정한다.
HPC에서는 NUMA 인식 MPI 구현과 NUMA 노드에 맞춘 프로세스 배치로 메모리 대역폭을 활용한다. 데이터베이스 시스템은 트랜잭션 처리 스레드를 코어에 고정하고, 쿼리 실행 계획과 버퍼 캐시를 NUMA 토폴로지에 맞춘다. 웹 서버는 워커 프로세스 바인딩, 네트워크 인터럽트 친화성, 요청 분산을 조합한다.
Kubernetes에서는 CPU Manager 정책, 보장된 Pod의 전용 코어 할당, NUMA 토폴로지 인식 스케줄링이 관련된다.
측정값을 보고 조정할 항목
튜닝에서는 CPU 사용률의 불균형, 캐시 미스율, NUMA 원격 메모리 접근 비율, 컨텍스트 스위칭 빈도를 확인한다. 워크로드 특성을 먼저 분석하고 벤치마크로 설정을 찾은 뒤, 프로덕션에는 점진적으로 적용하면서 계속 관찰하고 조정한다.
# CPU 별 부하 확인
mpstat -P ALL 1
# NUMA 메모리 통계
numastat -m
# 프로세스의 코어 친화성 확인
taskset -p <pid>
# 상세 성능 분석
perf stat -a sleep 10