NUMA (Kernel Level)

1,128 단어·3 분·원문(.md)

커널 레벨에서 NUMA를 이해하는것은 운영체제가 하드웨어의 불균형한 메모리 구조를 어떻게 추상화하고

성능 손실을 최소화하기 위해 자원을 어떻게 배치하는지 이해하는 것이다.

커널의 NUMA 추상화 Node, Zone #

리눅스 커널은 물리적 메모리를 관리하기 위해서 계층 구조를 사용한다.

  • Node(pg_data_t): 커널에서 NUMA 노드를 표현하는 최상위 구조체다. 각 노드마다 별도의 메모리 관리 구조를 가진다.
  • Zone: 각 노드 내의 메모리는 다시 ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM 등으로 나뉜다
  • Distance(SLIT): 커널은 ACPI의 SLIT(System Locality Information Table)를 참조하여 노드간의 거리(비용)을 수치화 한다. 로컬 접근 비용이 10이라면 이웃 노드는 20, 더 먼 노드는 30식으로 정의된다.

메모리 할당 정책 (Memory Allocation Policies) #

커널은 프로세스가 메모리를 요청할 때 어떤 노드에 줄지 결정하는 여러 정책을 제공한다.

  • Local Strategy(기본값): 프로세스가 현재 실행중인 cpu가 속한 노드에서 메모리를 할당받으려고 시도한다.
  • Interleave: 여러 노드에 페이지를 번갈아가며 할당한다, 특정 노드의 대역폭 부족 bottleneck을 방지할때 유리한다.
  • Preferred: 특정 노드를 선호하지만, 자리가 없으면 다른 노드에 할당한다.
  • Bind: 반드시 특정 노드들에 대해서만 할당받아야하며, 공간이없으면 OOM이다.

중요 개념: First Touch Policy 리눅스는 malloc() 호출 시점이 아니라, 실제로 해당 메모리에 데이터를 처음 쓸 때 물리 페이지를 할당한다. 이때 해당 쓰기 작업을 수행하는 cpu가 속한 노드에 메모리가 배치된다.

NUMA 스케줄링과 Locality #

cpu 스케줄러는 프로세스의 실행 위치를 결정할 때 메모리 위치를 고려해야 한다.

  • Task Migration의 딜레마: cpu 스케줄러가 부하 분산을 위해 프로세스를 다른 노드의 cpu로 옮기면, 기존에 사용하던 메모리는 이제 원격 메모리가 되어 성능이 떨어진다.
  • NUMA Balancing(Auto-NUMA): 커널의 배경 스레드(knumad)가 주기적으로 프로세스의 메모리 접근 패턴을 감시한다. 만약 프로세스가 다른 노드의 메모리를 더 많이 쓰고있다면 다음중 하나를 실행한다.
    • Page Migration: 메모리 페이지가 프로세스가 있는 노드로 옮긴다.
    • Task Migration: 프로세스를 메모리가 있는 노드로 옮긴다.

커널 파라미터 및 성능이슈 #

Zone Reclaim Mode #

특정 노드의 메모리가 부족할 때, 다른 노드의 남는 메모리를 쓸건지(Remote Access)

아니면 로컬 노드의 캐시를 비워서라도 로컬 메모리를 확보할 것인지(Reclaim) 결정해야한다.

데이터베이스 환경에서는 이를 잘못 설정하면 불필요한 캐시 삭제로 이냏 성능이 널뛰는 현상을 볼 수 있다.

Transparent Huge Pages(THP) #

큰 메모리 페이지(2MB등)를 사용하면 TLB 히트율은 올라가지만, NUMA 노드간 페이지 이동 Migration 사이시 오버헤드가 커지는 부작용이 있다.


커널 인터페이스로 NUMA의 상태를 확인할 수 있는 방법들이 있다.

numactl --hardware를 사용하면 노드별 cpu/memory 분포와 노드간의 distance를 확인할 수 있다.

numastat: 각 노드에서 메모리 할당이 성공했는지 (numa_hit), 타 노드에서 끌어왔는지 (numa_miss) 통계 확인이 가능하다.

/proc/selc/numa_maps: 특정 프로세스가 어떤 노드의 메모리를 얼마나 쓰고있는지 상세 분석가능하다.

Operating-System/numa2.md