[WAYLAND] GPU 동작 원리 (메모리 & 주소 공간 관리)

오늘은 최근에 관심을 받기 시작하고 있는 멀티 GPU 지원과 관련된 prime (dma-buf) 기능을 정확하게 이해하기 위해 필수적으로 알아야 하는 GPU 의 메모리 및 주소 공간 관리에 대해 살펴보고자 한다. 설명은 NVIDIA 의 KEPLER 아키텍처와 MESA/DRM (nouveau) 소프트웨어 스택을 기준으로 할 계획이다.

우선 메모리(memory)와 주소 공간(address space)이 무엇인지에 대해 간단히 살펴보자. 메모리는 실제 필요한 데이터를 저장하는 물리적인 저장 장치를 의미한다. 일반적으로 CPU 가 사용하는 메인메모리(RAM)가 있고, 데스크탑용 GPU 의 경우에는 자체 메모리(VRAM)를 갖고 있다. (일반적으로 ARM 은 GPU 가 자체 메모리를 갖고 있지 않고 메인메모리를 같이 사용한다.) 현재 우리가 사용하고 있는 컴퓨팅 아키텍처에서 가장 중요한 연산 장치(CPU)와 저장 장치(memory) 중의 하나라고 볼 수 있다. 그렇다면 주소 공간은 무엇일까? 바로 연산 장치가 저장 장치의 특정 위치를 접근할 때 사용하는 주소를 의미한다. 리눅스 커널을 공부했거나 운영체제 수업을 들었던 사람이라면 가상 주소, 물리 주소, 페이징 등에 대해 들어봤을텐데, 이러한 주소 공간이 CPU 를 위해서만 사용되는 것이 아니고, GPU 가 나날이 발전하고 그 역할이 커짐에 따라 GPU 또한 주소 공간을 관리하는 기능을 가지게 되었다. (사실 완전한 형태의 IOMMU 를 지원하기 시작한건 NVIDIA 의 경우 TESLA 아키텍처 이후 정도라고 생각하면 되겠다.)

그렇다면 GPU 가 주소 공간 관리를 지원할 수 있게 된 계기가 무엇일까? 여러 가지 이유가 있겠지만 필자가 생각하는 가장 중요한 계기는 PCI express 와 64 bit 지원이다. PCI express 의 보편화는 GPU 가 빠른 속도로 메인메모리에 직접 접근할 수 있는 계기를 만들어주었고, 64 bit 지원은 물리 주소 공간의 확장으로 메인메모리와 그래픽메모리를 하나의 주소 공간 안에 넣을 수 있게 해주었다. (예전 32 bit 환경에서는 하나의 물리 주소 공간이 4 GBytes 밖에 되지 않았기 때문에 그보다 많은 메모리는 하나의 물리 주소 공간에서 사용할 수 없었다.) 이러한 발전된 기술을 기반으로 현재 컴퓨팅 환경은 CPU 와 GPU 가 사용하는 물리 주소 공간이 하나로 통합된 UMA (Unified Memory Architecture) 를 사용한다. 이를 통해 GPU 는 자유롭게 메인메모리와 그래픽메모리에 접근할 수 있게 되었다.

위에서 설명한 것처럼 UMA 를 통해 물리 주소 공간은 하나로 통합되었으니 큰 산 하나는 넘었다고 볼 수 있다. 그렇다면 가상 주소 공간은 어떻게 할까? 페이징을 공부해본 사람이라면 알겠지만 가상 주소 공간이 가지는 장점은 크게 세 가지(독립된 주소 공간 제공, 메모리 단편화 제거, 메모리 보호) 이다. 이 중에서 GPU 입장에서 가장 중요한 것은 바로 메모리 단편화 제거이다. 이와 관련해서 잠깐만 옛날 얘기를 하면, GPU 가 메인메모리에 직접 접근할 필요가 있다고 느끼게 된 이유는 텍스쳐와 같은 대용량의 데이터가 널리 쓰이기 시작하면서 메인메모리와 그래픽메모리를 효과적으로 관리할 필요가 생겼기 때문이다. 하지만 GPU 가 메인메모리에 직접 접근하기 위해서는 메인메모리의 단편화 문제를 해결해야 했다. (CPU 가 페이징을 이용해 단편화 문제를 해결하는 것처럼…) 이를 해결하기 위한 첫 번째 시도가 바로 AGP 에 포함되었던 GART (Graphics Address Remapping Table) 였다. 이는 간단히 얘기하면 제한적인 IOMMU 라고 보면 된다. 노스 브릿지 (north bridge) 에 있는 GART 테이블에 필요한 값을 세팅하면 특정 물리 주소를 다른 물리 주소로 변환해 주는 역할을 하였다. 그래서 메인메모리의 파편화되어있는 페이지들을 GART 를 통해 연속된 물리 주소 공간에 할당해놓고 GPU 가 사용할 수 있게 해주었던 것이다. 하지만 이는 어디까지나 임시 방편이었고, 최근에는 대략(?) TESLA 이후부터 완전한 형태의 IOMMU 기능을 제공하기 시작하였다. 즉, 현재는 GPU 도 CPU 처럼 임의의 가상 주소 공간을 생성해서 자유롭게 물리 주소 공간을 할당하는 것이 가능해졌다는 것이다. 다만 차이점이라면 CPU 는 프로세스 별로 가상 주소 공간을 제공하지만 GPU 는 채널 별로 가상 주소 공간을 제공한다. (채널은 GPU 가 제공하는 하나의 명령어 스트림을 실행하는 통로라고 보면 된다.) 그래서 보통 3D 프로그램이 새로운 OPENGL 컨텍스트를 생성하면, MESA/DRM 을 통해 새로운 채널을 하나 할당받고 거기에 사용할 페이지 디렉토리를 같이 생성해서 등록한다. (그리고 기본적인 페이지 폴트 처리는 하지만, 사용할 페이지는 미리 할당해놓고 사용한다.) 이를 그림으로 간단히 표현하면 아래와 같다.

gart

위의 그림에서 나타낸 것처럼, UMA 를 통해 메인메모리와 그래픽메모리는 하나의 물리 주소 공간에 공존한다. 그리고 CPU 는 MMU 가 만들어주는 가상 주소 공간을 통해 물리 주소 공간에 접근하고, GPU 는 GART(IOMMU) 가 만들어주는 가상 주소 공간을 통해 물리 주소 공간에 접근할 수 있게 된다.

여기까지가 GPU 의 기본적인 물리/가상 주소 공간 관리에 대한 설명이다. 이제 이를 통해 어떻게 멀티 GPU 를 효과적으로 지원할 수 있을지에 대해 간단히 살펴보고 마무리하겠다.

현재의 GPU 는 기본적으로 메인메모리와 그래픽메모리 모두 직접 접근이 가능하다. 그래서 현재 MESA/DRM(nouveau) 의 경우에는, 텍스쳐, 버텍스와 같은 버퍼와 명령어 스트림 버퍼를 메인메모리에 할당하고, 프레임버퍼 또한 메인메모리에 할당한다. (프레임버퍼는 디스플레이에서 스캐아웃(scan-out) 요청이 왔을 때 바로 넘겨줘야 하기 때문에 접근 속도가 더 빠른 그래픽메모리에 둔다는 얘기를 본적이 있는데 현재 MESA/DRM(nouveau) 코드에는 일단 메인메모리에 할당해서 사용하고 있다.) 이렇듯, 대부분의 버퍼와 프레임버퍼까지 메인메모리에 할당해서 사용하기 때문에 멀티 GPU 를 지원하는 것은 당연히 훨씬(!) 단순해졌다.

현재 MESA/DRM 에서 제공하는 prime (dma-buf) 는 크게 두 단계로 나뉜다. 첫 번째 단계는 버퍼를 제공하는 GPU 가 익스포트(export) 하는 단계이다. 이 단계에서 버퍼가 메인메모리에 있다면 특별히 추가적으로 할 일없이 해당 페이지 목록을 저장해놓는다. 그리고 만약에 해당 버퍼가 그래픽메모리에 있다면 메인메모리로 복사한 다음, 해당 페이지 목록을 저장해놓는다. (그래픽메모리를 메인메모리로 복사하는 일은 CPU 가 복사 명령어를 GPU 에게 스트림으로 보내면 GPU 가 실제 복사를 수행한다.) 그리고 두 번째 단계는 버퍼를 사용할 GPU 가 임포트(import) 하는 단계이다. 이 단계는 앞의 단계보다 더 간단하다. 왜냐하면 공유할 버퍼는 메인메모리에 이미 올라와있는 상태이기 때문이다. (앞에서 익스포트 단계에서 그래픽메모리에 있는 버퍼는 메인메모리로 미리 옮겨놓는다고 설명하였다.) 이 단계에서는 해당 채널에서 사용하고 있는 가상 주소 공간에 필요한만큼의 공간을 할당받고 공유할 버퍼의 페이지들을 연결시켜주기만 하면 된다. 즉, 현재의 MESA/DRM 에서는 대부분의 버퍼를 메인메모리에 두고 사용하기 때문에 불필요한 복사 없이 페이지 디렉토리(테이블)에 필요한 값만 설정해주면 공유가 끝난다는 것이다.

사실 멀티 GPU 지원은 NUMA (Non-Uniform Memory Address) 에 가까운 문제이다. 왜냐하면 GPU 별로 내장 메모리를 사용하기 때문이다. 하지만 공유 가능한 메인메모리를 많이 사용하는 환경이 되면 자연스럽게 UMA (Uniform Memory Address) 문제가 된다. 이는 멀티 GPU 지원 문제를 훨씬 단순화시키게 된다. 그리고 앞으로도 GPU 가 이런 상황을 염두해두고 발전할 가능성이 높기 때문에 이 문제는 더 간단해질 것이다. (이 문제에 가장 적극적인 회사가 NVIDIA 이다.)

지금까지 GPU 의 메모리 및 주소 공간 관리와 이를 이용한 멀티 GPU 간의 메모리 공유에 대한 내용을 살펴보았다. 결론은 GPU 가 하는 일이 많아짐에 따라 CPU 가 사용하던 주소 공간 관리 방식을 차용하기 시작했고, 이를 통해 메모리 관리나 멀티 GPU 지원이 훨씬 간단해지고 있다는 것이다.

댓글 남기기