/vm/numa.rst: What is NUMA

Linux 설명서의 본진이라 생각되는 kernel.org/doc/Documentation 의 문서들을 시간날 때 조금씩 보려고 합니다. 영어를 잘 못하지만 DeepL을 이용해서 기계번역하고, 좀 어색해보이는 부분은 일부 수정(과번역 부분 영어로 다시 영어로 교체, 어색한 문장 수정 등)했습니다.
출처: https://www.kernel.org/doc/Documentation/vm/numa.rst

1999년 11월에 Kanoj Sarcar kanoj@sgi.com에 의해 시작되었습니다.

What is NUMA?

이 질문은 두 가지 관점에서 답변할 수 있습니다: 하드웨어 관점과 Linux 소프트웨어 관점입니다.

하드웨어 관점에서 NUMA 시스템은 여러 구성 요소나 어셈블리로 구성된 컴퓨터 플랫폼으로, 각 구성 요소나 어셈블리에는 0개 이상의 CPU, 로컬 메모리, 및/또는 I/O 버스가 포함될 수 있습니다. 간결성을 위해 및 이러한 물리적 구성 요소/어셈블리의 하드웨어 관점을 소프트웨어 추상화로부터 구분하기 위해, 이 문서에서는 구성 요소/어셈블리를 ‘셀(cell)’이라고 부르겠습니다.

각 ‘셀’은 시스템의 SMP [대칭형 다중 프로세서(symmetric multi-processor)] 하위 집합으로 볼 수 있지만, 독립형 SMP 시스템에 필요한 일부 구성 요소가 특정 셀에 존재하지 않을 수 있습니다. NUMA 시스템의 셀은 어떤 형태의 시스템 인터커넥트로 연결됩니다. 예를 들어, 크로스바나 포인트-투-포인트 링크는 NUMA 시스템 인터커넥트의 일반적인 유형입니다. 이 두 유형의 인터커넥트는 여러 셀이 다른 셀로부터 다양한 거리에 위치할 수 있는 NUMA 플랫폼을 구성하기 위해 집계될 수 있습니다.

Linux의 경우, 관심 있는 NUMA 플랫폼은 주로 Cache Coherent NUMA 또는 ccNUMA 시스템입니다. ccNUMA 시스템에서는 모든 메모리가 어떤 셀에 연결된 CPU에서도 가시적이고 접근 가능하며, 캐시 일관성은 프로세서 캐시와/또는 시스템 인터커넥트에 의해 하드웨어적으로 처리됩니다.

메모리 액세스 시간과 효과적인 메모리 대역폭은 메모리 액세스를 수행하는 CPU 또는 IO 버스가 포함된 셀과 대상 메모리가 포함된 셀 간의 거리에 따라 달라집니다. 예를 들어, 동일한 셀에 연결된 CPU가 메모리에 액세스할 경우 다른 원격 셀의 메모리에 액세스하는 것보다 더 빠른 액세스 시간과 높은 대역폭을 경험합니다. NUMA 플랫폼은 특정 셀로부터 여러 원격 거리에 위치한 셀을 가질 수 있습니다.

플랫폼 벤더들은 소프트웨어 개발자의 생활을 흥미롭게 만들기 위해 NUMA 시스템을 구축하는 것이 아닙니다. 대신 이 아키텍처는 확장 가능한 메모리 대역폭을 제공하는 수단입니다. 그러나 확장 가능한 메모리 대역폭을 달성하려면 시스템 및 응용 프로그램 소프트웨어는 메모리 참조[캐시 미스]의 대부분이 “로컬” 메모리—동일 셀 내 메모리(존재할 경우)—또는 가장 가까운 셀의 메모리로 이루어지도록 구성해야 합니다.

이것이 Linux 소프트웨어가 NUMA 시스템을 보는 방식입니다:

Linux는 시스템의 하드웨어 리소스를 “노드(node)”라고 불리는 여러 소프트웨어 추상화로 나누어 관리합니다. Linux는 노드를 하드웨어 플랫폼의 물리적 셀에 매핑하며, 일부 아키텍처에서는 일부 세부 사항을 추상화합니다. 물리적 셀과 마찬가지로 소프트웨어 노드에는 0개 이상의 CPU, 메모리 및/또는 I/O 버스가 포함될 수 있습니다. 또한, “더 가까운(closer)” 노드(더 가까운 셀에 매핑되는 노드)에 대한 메모리 액세스는 일반적으로 더 먼 셀에 대한 액세스보다 더 빠른 액세스 시간과 “더 가까운” 노드(더 가까운 셀에 매핑된 노드)에 대한 액세스는 일반적으로 더 먼 셀에 대한 액세스보다 더 빠른 액세스 시간과 더 높은 효과적 대역폭을 경험합니다.

일부 아키텍처(예: x86)에서는 Linux가 메모리가 연결되지 않은 물리적 셀을 대표하는 노드를 숨기고, 해당 셀에 연결된 CPU를 메모리가 연결된 셀을 대표하는 노드로 재할당합니다. 따라서 이러한 아키텍처에서는 Linux가 특정 노드와 연관시킨 모든 CPU가 동일한 로컬 메모리 액세스 시간과 대역폭을 가질 것이라고 가정할 수 없습니다.

또한 일부 아키텍처(예: x86)에서는 Linux가 추가 노드의 에뮬레이션을 지원합니다. NUMA 에뮬레이션 시 Linux는 기존 노드(또는 NUMA가 아닌 플랫폼의 시스템 메모리)를 다중 노드로 분할합니다. 각 에뮬레이션된 노드는 기본 셀의 물리적 메모리 일부를 관리합니다. NUMA 에뮬레이션은 non-NUMA 플랫폼에서 NUMA 커널 및 애플리케이션 기능을 테스트하는 데 유용하며, cpusets와 함께 사용할 때 메모리 리소스 관리 메커니즘으로도 활용됩니다. 참조 Documentation/admin-guide/cgroup-v1/cpusets.rst

메모리를 가진 각 노드에 대해 Linux는 독립적인 메모리 관리 서브시스템을 구축하며, 자체 자유 페이지 목록, 사용 중 페이지 목록, 사용 통계 및 액세스를 조정하는 잠금 장치를 포함합니다. 또한 Linux는 각 메모리 존 (DMA, DMA32, NORMAL, HIGH_MEMORY, MOVABLE 중 하나 이상)에 대해 정렬된 “존 목록”을 구축합니다. 존 목록은 선택된 존/노드가 할당 요청을 충족시킬 수 없을 때 방문할 존/노드를 지정합니다. 이 상황, 즉 존이 요청을 충족시킬 수 있는 사용 가능한 메모리가 없을 때를 “오버플로우(overflow)” 또는 “폴백(fallback)”이라고 합니다.

일부 노드에는 서로 다른 유형의 메모리를 포함하는 여러 존이 존재하기 때문에 Linux는 존 목록을 순서대로 배열할 때 할당 요청이 다른 노드의 동일한 존 유형으로 되돌아가도록 할지, 아니면 동일한 노드의 다른 존 유형으로 되돌아가도록 할지 결정해야 합니다. 이는 DMA 또는 DMA32와 같은 일부 존이 상대적으로 희소한 자원을 나타내기 때문에 중요한 고려 사항입니다. Linux는 기본적으로 노드 순서대로 배열된 존 목록을 선택합니다. 이는 동일한 노드의 다른 존으로 먼저 후퇴하려고 시도한 후 NUMA 거리로 정렬된 원격 노드를 사용하는 것을 의미합니다.

기본적으로 Linux는 요청을 실행하는 CPU가 할당된 노드에서 메모리 할당 요청을 충족시키려고 시도합니다. 구체적으로, Linux는 요청이 발생한 노드의 적절한 존 목록에서 첫 번째 노드에서 할당을 시도합니다. 이는 “로컬 할당(local allocation)”이라고 합니다. “로컬” 노드가 요청을 충족시킬 수 없는 경우 커널은 선택된 존 목록의 다른 노드 존을 검사하여 목록에서 요청을 충족시킬 수 있는 첫 번째 존을 찾습니다.

로컬 할당은 할당된 메모리에 대한 후속 액세스를 기본 물리적 리소스에 “로컬”로 유지하고 시스템 인터커넥트에서 벗어나게 합니다–커널이 메모리를 할당받은 작업이 나중에 해당 메모리에서 이동하지 않는 한. Linux 스케줄러는 플랫폼의 NUMA 토폴로지를 인식합니다. 이는 “스케줄링 도메인(scheduling domain)” 데이터 구조체(참조: see Documentation/scheduler/sched-domains.rst에 구현되어 있으며, 스케줄러는 작업의 원격 스케줄링 도메인 이동을 최소화하려고 시도합니다. 그러나 스케줄러는 작업의 NUMA 발자국을 직접 고려하지 않습니다. 따라서 충분한 불균형이 발생하면 작업이 초기 노드와 커널 데이터 구조체에서 멀리 떨어진 노드로 이동할 수 있습니다.

시스템 관리자와 애플리케이션 설계자는 CPU 친화성 명령줄 인터페이스(예: taskset(1) 및 numactl(1))와 프로그램 인터페이스(예: sched_setaffinity(2))를 사용하여 작업의 이동을 제한하고 NUMA 로컬리티를 개선할 수 있습니다. 또한 Linux NUMA 메모리 정책을 사용하여 커널의 기본 로컬 할당 동작을 수정할 수 있습니다. 참조: Documentation/admin-guide/mm/numa_memory_policy.rst .

시스템 관리자는 제어 그룹과 CPU 세트(CPUsets)를 사용하여 비특권 사용자가 스케줄링 또는 NUMA 명령 및 기능에서 지정할 수 있는 CPU와 노드의 메모리를 제한할 수 있습니다. 참조: Documentation/admin-guide/cgroup-v1/cpusets.rst

메모리 없는 노드를 숨기지 않는 아키텍처에서 Linux는 존리스트에 메모리를 가진 존(노드)만 포함합니다. 이는 메모리 없는 노드의 경우 “로컬 메모리 노드”—CPU 노드의 존리스트에 있는 첫 번째 존의 노드—가 해당 노드 자체는 아니라는 의미입니다. 대신, 커널이 존리스트를 생성할 때 메모리를 가진 가장 가까운 노드로 선택한 노드가 될 것입니다. 따라서 기본적으로 로컬 할당은 커널이 가장 가까운 사용 가능한 메모리를 공급함으로써 성공합니다. 이는 메모리를 가진 노드가 오버플로우될 때 다른 가까운 노드로 할당이 전환되는 동일한 메커니즘의 결과입니다.

일부 커널 할당은 이러한 할당 후퇴 행동을 원하지 않거나 허용하지 않습니다. 대신 지정된 노드에서 메모리를 확보하거나 해당 노드가 사용 가능한 메모리가 없다는 알림을 받기를 원합니다. 이는 예를 들어 서브시스템이 CPU별 메모리 자원을 할당할 때 일반적으로 발생하는 경우입니다.

이러한 할당을 수행하는 일반적인 모델은 커널의 numa_node_id() 또는 CPU_to_node() 함수를 사용하여 “현재 CPU”가 연결된 노드의 노드 ID를 획득한 후 반환된 노드 ID로부터만 메모리를 요청하는 것입니다. 이러한 할당이 실패하면 요청한 서브시스템은 자체 백업 경로로 전환할 수 있습니다. 슬래브 커널 메모리 할당기가 이 예시입니다. 또는 서브시스템은 할당 실패 시 자체적으로 비활성화하거나 활성화하지 않을 수 있습니다. 커널 프로파일링 서브시스템이 이에 해당됩니다.

아키텍처가 메모리리스(memoryless) 노드를 지원하지 않는 경우(즉, 메모리리스 노드를 숨기지 않는 경우), 메모리리스 노드에 연결된 CPU는 항상 대체 경로 오버헤드를 발생시키거나, 일부 서브시스템은 메모리가 없는 노드에서만 메모리를 할당하려고 시도할 경우 초기화 실패가 발생할 수 있습니다. 이러한 아키텍처를 투명하게 지원하기 위해 커널 서브시스템은 numa_mem_id() 또는 cpu_to_mem() 함수를 사용하여 호출하거나 지정된 CPU의 “로컬 메모리 노드”를 찾을 수 있습니다. 다시 말해, 이는 기본적으로 로컬 페이지 할당이 시도되는 동일한 노드입니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like
Read More

Documentation/sysctl/net.txt

출처: https://www.kernel.org/doc/Documentation/sysctl/net.txt"busy poll timeout" 번역 표현이 참 어색합니다. 적당한 한글 표현을 못 찾겠습니다. proc/sys/net/*에 대한 문서(c) 1999 Terrehon…
Read More

/sysctl/fs.txt

/proc/sys/fs/*에 대한 문서 커널 버전 2.2.10(c) 1998, 1999, Rik van Riel riel@nl.linux.org(c) 2009, Shen Fengshen@cn.fujitsu.com 일반 정보 및…
Read More

Zram 성능 분석

Blog를 보다가 관심 가는 글이 보여서 AI번역+약간 교정해 보았습니다.출처: https://notes.xeome.dev/notes/Zram 소개 Zram은 압축된 가상 메모리 블록 장치를 활용하여…
Read More

/sysctl/vm.txt

RHCA 달리던 중에 "RH442 Performance Tuning Linux in Physical Virtual and Cloud"라는 과목이 있습니다. 보던 중 sysctl을 이용해서…