Lecture Summary/CUDA

[CUDA] Multi-Process Service

H/W engineer_ beginner 2022. 1. 26. 18:32
SMALL

[1] Introduction : 

 Multi Process Service (MPS) 개념은 NVIDIA의 Maxwell architecture에서 처음 고안된 방법으로 GPU의 resource utilization 저하 문제를 해결하여 성능 향상(Throughput 증가)을 이끌어내기 위하여 제시되었습니다. GPU가 단순히 그래픽 처리만을 하는 것이 아니라 General purpose computing을 하기 위해 발전해오며 HPC(High Purpose Computing) 응용 프로그램들을 처리하거나 Multi-tenant 환경에서 복잡한 여러 DNN 연산을 수행해야 하는 경우가 생겼습니다. 하여 NVIDIA에서는 한정된 자원을 효과적으로 사용하기 위한 많은 방법을 제시하였으며 ( Stream, Hyper-Q 등 ) 다수의 Process가 하나의 GPU에서 실행될 수 있도록 돕는 런타임 서비스 : MPS 를 도입하였습니다. 

 

 MPS는 여러 CUDA process 들이 단일 GPU *Context를 공유할 수 있도록 하는 기능입니다. MPS에 의해 각 프로세스들은 GPU 분할 사용이 가능하도록 사용 공간을 나누어 할당 받게 됩니다. 이를 통해 GPU의 utilization을 최대화 하는 동시에 각 process 들의 kernel 과 memcopy 연산이 동시에 실행될 수 있도록 합니다. MPS 개념 이전에 등장했던 Stream과 Hyper-Q의 개념을 간단히 정리해보자면, Stream이란 CUDA 실행을 위한 비동기 객체로, 각 Stream 내부의 작업들은 순차적으로 실행되며, 각 Stream 들은 디바이스의 자원이 중첩되지 않는 선에서 동시 실행되게 됩니다. Stream을 통해 여러 작업을 서로 다른 Stream으로 분류해 실행하더라도 실제 GPU의 Scheduling queue (work queue)는 하나였기 때문에 False dependency problem이 발생할 수 있습니다. 하여 이를 해결하기 위해 Hyper-Q라는 Multi work queue 개념이 등장하였습니다. Hyper-Q란 다수의 Hardware work queue를 생성하는 Grid management unit을 도입하여 각 Stream이 같은 pipeline으로 유지되게 함으로써 dependency 문제가 없는 Scheduling을 가능하게 했습니다.  

 

[Figure 1] Hyper-Q : Multiple Hardware Work Queues

 

 Hyper-Q를 사용함으로써 device 의 resource utilization은 상당히 개선될 수 있었지만 Hyper-Q를 사용하기 위해서는 task들이 반드시 같은 context 상에 속해야 한다는 제한이 있었습니다. 따라서 다른 어플리케이션 실행이 하나의 device에서 이뤄질 수 있도록 MPS(Multi Process Service) 라는 새로운 개념을 도입하게 되었습니다.

 

 MPS는 서로 다른 커널을 서버에 제출함으로써 서로 다른 Context의 커널들이 단일 Context 내에서 수행되는 것 처럼 보이도록 했습니다. 

 

[Figure 2] How MPS work?

 

 위의 그림은 MPS의 전체적인 Work load를 설명하는 그림입니다. Control demon process에 의해 MPS Server가 시작되고, Server에 의해 만들어진 실행 환경에 MPS client 가 각 kernel의 context를 순차적으로 제출합니다. 이 실행환경은 Shared context로 실행될 수 있기 때문에 MPS server는 client의 요청을 대표하여 디바이스에 작업들을 동시에 실행시킵니다. 이를 통해 여러 개의 CUDA process들이 하나의 GPU context를 공유하는 것이 가능하게 됩니다. MPS 를 통한 Shared context는 원래 여러 개로 나누어져 있던 디바이스의 데이터 저장 공간 혹은 Scheduling unit 또한 서로 공유 될 수 있다는 것을 의미합니다. 이를 통해 MPS 이전에 GPU가 Process 들을 시분할 방식으로 실행할 때 발생하는 overhead를 획기적으로 줄일 수 있으며 결과적으로 MPS를 통한 resource utilization이 향상되고 실행 시간을 감소시킬 수 있습니다.

 

 하지만 여러 MPS의 client가 동시에 실행되는 것과는 달리 MPS의 sever는 오직 한 user만이 실행시킬 수 있습니다. 다시 말해 같은 UID로 서버에 제출된 client만이 GPU에서 동시 실행 될 수 있습니다. 때문에 MPS의 clinet들 중 하나가 비정상적으로 종료된다면, MPS server와 다른 client들이 어떤 상태였는지 알 수 없게 됩니다.

 

 Maxwell architecture에서 처음 제안되었던 MPS는 Volta architecuture가 도입되며 크게 변화했습니다. Volta 이전의 MPS에서는 각 client가 device 주소 공간을 MPS server를 통해 공유하였지만, Volta architecture에서는 Server 대신, 직접 device 에 작업을 제출할 수 있도록 지원하고, 각 cleint를 위한 독립적인 주소 공간을 제공합니다. 또 QoS (Quality of Service)를 위해 실행 자원량을 유기적으로 제한 할 수 있습니다. 

 

[Figure 3] Volta architecture 이전의 MPS (좌), Volta architecture에 적용된 MPS (우)

 

 Maxwell architecture에서 처음 도입된 MPS 는 Architecutre의 발전과 함께 발전해왔습니다. 하여 본 포스팅을 통해 MPS의 발전 과정에 대해 짚어보고, 가장 다이나믹한 변화를 이끈 Volta architectre의 MPS 구조에 대해 집중적으로 논의해보려 합니다. 

 

 

 

*Context : 실행되고 있는 Process 및 Thread의 상태 정보


 

[2] When to use MPS : 

 MPS 기술은 크게 세 가지의 장점을 사용자에게 제공합니다.

 

   i) GPU utilization

   ii) Reduced on-GPU cintext storage

   iii) Reduced GPU context switching

 

i) GPU utilization

 GPU에서 하나의 process 만을 수행된다면 모든 연산을 위한 memory-bandwidth 용량 및 사용 가능한 GPU의 resource들을 최대로 사용할 수 없을 것입니다. 때문에 MPS를 사용하여 여러 process들의 kernel과 memcopy 연산들이 GPU 상에서 overlap되게 하여 GPU의 높은 utilization을 확보하고 더 빠른 running time으로 process 들을 처리 하는 것이 가능해집니다.

 

ii) Reduced on-GPU cintext storage

 MPS를 사용하지 않는다면, 여러 CUDA process들은 구별된 각각의 storage와 scheduling resource들을 할당 받을 것 입니다. 하지만 MPS를 사용하게 된다면 MPS server는 모든 clinets가 하나의 storage와 scheduling resource들을 공유할 수 있습니다. 특히 Volta architecture부터는 MPS clinet들의 독립된 메모리 공간을 사용하도록 지원하여 GPU이 resource 사용을 더 현저하게 줄일 수 있었습니다.

 

iii) Reduced GPU context switching

 MPS를 사용하지 않는다면, Multi processing 수행 시 각 process 들을 위한 scheduling resource들이 모두 지워졌다가 다시 사용되기를 반복해야 합니다. 이는 GPU 실행 성능에 큰 영향을 미치게 됩니다. 하지만 MPS를 사용한다면 MPS server는 모든 clinet들에게 하나의 scheduling resource 세트를 제공하여 여러 clinet가 swiping 될 때 발생하는 overhead를 획기적으로 줄였습니다.

 

 

 하지만 MPS의 client-sever connection에는 정해진 한계가 존재합니다. pre-Volta architecture에서는 각 device 당 16 client CUDA context 까지 지원하며 Volta architecture의 MPS sever의 경우는 48 clinet CUDA context 까지 지원합니다. 만약 connection limit을 넘은 요청을 실행하면 MPS server에 'failed connection' log가 생성될 것입니다.

 

 

 

 

 


 

[3] MPS Architecture : 

 CUDA란 General purpose를 위한 병렬 컴퓨팅 플랫폼입니다. NVIDIA에서는 많은 복잡한 문제들을 CPU보다 GPU에서 훨씬 효율적으로 처리하기위하여 병렬 컴퓨팅 엔진을 위한 Programming model을 제시합니다. 

 

CUDA program은 CUDA context를 생성하는 것으로 시작됩니다. context는 CUDA program이 해당 GPU의 메모리를 관리하고 작업을 시작하는데 필요한 모든 하드웨어 리소스를 캡슐화합니다.   

 

 GPU에서 작업이 시작되는 것은 일반적으로 할당된 GPU의 메모리 공간에 필요한 데이터를 복사하는 것과 그 데이터 위에서 CUDA kernel이 실행되는 것, 그리고 나서 GPU 메모리에서 시스템 메모리 공간으로 그 결과를 다시 복사하는 것을 포함합니다. 

 

 GPU에서 CUDA를 통해 실행되는 모든 작업들은 CUDA stream(병렬 실행을 위한 비동기 객체) 이라고 불리는 공간으로 이동하게 됩니다. Stream이란 여러 kernel의 Command들을 순서대로 처리하기 위한 S/W 추상화 개념입니다. 만일 작업이 서로 다른 두 Stream으로 실행된다면 이 둘은 병렬적으로 동시에 처리가 가능합니다.

 

 CUDA stream은 GPU의 driver에 의해 하나 혹은 그 이상의 'work queue'라는 이름으로 불리 수 있습니다. Work queue란 GPU의 특정 engine에 의해 실행될 Stream의 command의 순서를 나타내는 H/W resource 입니다. 하지만 하나의 work queue만을 사용해 생기는 비효율이 확인되었고 Hyper-Q라는 Multi work queue 개념이 등장하게 되었습니다. Hyper-Q란 다수의 Hardware work queue를 생성하는 Grid management unit을 도입하여 각 Stream이 같은 pipeline으로 유지되게 함으로써 dependency 문제가 없는 Scheduling을 가능하게 했습니다. 

 

 GPU는 time sliced scheduler를 갖고있습니다. 일반적으로 서로 다른 CUDA context가 존재하는 work queue에서 실행된 작업은 동시에 실행 될 수 없습니다. 따라서 이 scheduler를 사용하여 work queue에서 다른 CUDA context들이 실행 될 수 있도록 scheduling 합니다. 

 

 

[Figure 4] MPS가 적용되지 않은 MPI scheduling 방법

 

 위의 diagram은 MPS를 사용하지 않고 Multiple OS process가 포함된 MPI application을 수행할 때 CUDA kernel들이 scheduling 되는 방법을 설명하고 있습니다. 각 MPI process 들에 속한 CUDA kernel들은 동시에 예약 될 수 있지만 각 MPI process 들은 time-slice scheduler에 의해 순서대로 실행됩니다. 따라서 MPS가 적용되지 않은 환경에서는 동시에 여러 kernel을 실행하는 것이 불가능 했습니다.

 

 

 

[Figure 5] Volta architecture 이전의 MPS를 적용한 Client-server architecture

 

 Volta architecture 이전의 MPS 를 적용한 Client-sever architecture의 다이어그램은 다음과 같습니다. MPS server를 이용하여 여러개의 context들을 공유하는 하나의 CUDA context는 하드웨어 resource를 관리하게 됩니다. MPS client에 포함된 CUDA context는 MPS server를 통해 작업을 퍼널링 하게 되며 이를 통해 Client의 CUDA context들은 time sliced scheduling과 관련된 H/W 제한을 우회하고 동시에 다른 Kernel들을 실행 시킬 수 있습니다.

 

Volta architectre 에서부터는 새로운 Hardware capabilities를 제안합니다. 이를 통해 MPS server가 관리하던 Hardware 공간을 획기적으로 줄일 수 있었습니다. 한 client CUDA context는 대부분의 하드웨어 resource를 사용할 수 있게 되며, 작업을 하드웨어에 직접 전달해 실행시킬 수 있습니다. Volta MPS sever는 Hardware resource에서 실제로 실행되는 데에 사용되는 공간을 제외한 최소한의 공간만을 사용하게 되며, 개별 client가 제출한 작업을 동시에 예약하고 필요한 나머지 Hardware resource들을 조정합니다. 

 

 

[Figure 6] System-wide provisionging with multiple users (Provisioning Sequence)

 

 

 

 


 

[4] Background

 

(1) Process vs Thread

 

 Process란 실행을 위하여 메모리에 Load된 Program을 의미하며, OS에 의해 자원을 할당받는 작업의 단위 입니다. Thread는 이것보다 더 작은 단위의 작업으로, 할당 받은 자원을 이용하는 실행의 단위를 의미합니다. 따라서 하나의 Process 내에는 1개 이상의 Thread가 발생할 수 있습니다. 즉, Application 하나의 실행이 Process가 되며 Process 내에서 분기 실행 되는 단위가 Thread 입니다.

 

[Figure 7] Process

 

 Process는 위의 그림과 같이 각각의 독립된 메모리 영역을 할당 받습니다. (Code, Data, Stack, Heap 등) 각 Process는 별도의 주소 공간을 할당 받으며 일반적으로 다른 Process의 변수나 자료 구조에 접근할 수 없습니다. 따라서 다른 Process 의 자원에 접근하기 위해서는 Process 간 통신 방법인 IPC (Inter-Process Comunication)을 사용해야 합니다. 

 

 Thread란 Process 내에서 실행되는 여러 흐름의 단위로, Process 가 할당받은 자원을 사용하는 실행의 단위입니다. 

 

[Figure 8] Thread

 위의 그림처럼 각 Thread는 Process 내에서 Code, Data, Heap 영역을 공유하게 됩니다. (Stack 영역을 Thread 별로 따로 할당 받음) Thread는 Process 와 다르게 한 Process 내의 자원이나 주소 공간을 공유하며 실행되게 됩니다. 각 Thread는 유일하게 따로 할당받은 Stack 영역과 레지스터를 갖고 있지만 나머지 공간들이 서로 공유되어 Process의 더 유연한 실행을 가능하게 합니다.

 

(2) Multi process vs Multi thread

 

[Figure 9] Multi process vs Multi thread

 

 Multi-processing란 하나의 응용 프로그램을 수행하기 위하여 프로그램을 여러 개의 Process로 나누어 실행하는 방법으로, 각 프로세스가 하나의 작업(Task)를 수행하게 됩니다. Multi process 환경에서는 최초의 단일 프로세스를 부모 Process라 하며 부모 Process에서 파생된 다양한 자식 Process가 존재하게 됩니다. 때문에 여러 자식 Process 중 하나가 문제가 발생하더라도 다른 Task에 영향을 끼치지 않는다는 장점이 존재합니다. Multi process를 H/W적인 관점에서 바라보게 되면 Context switching 개념을 이해해야 합니다. Context switching이란 Processor(CPU)에서 여러 Process들을 번갈아 가며 실행하는 행위를 의미합니다. 즉 지금 실행되는 Process의 상태 (Context)를 잠시 저장하고 대기하던 다른 Process를 실행하게 되며 이 과정에서 이전에 보관하였던 이전 Process의 상태 (Context)가 다시 복구됩니다. Multi process를 실행하게 된다면 Context switching 과정에서 overhead가 발생할 우려가 존재합니다. 각 Process들은 각각의 독립 메모리 공간을 할당받기 때문에 Context switching 수행 시 Cache에 있는 모든 Data를 삭제하고 새롭게 불러와야 합니다. 이는 각 Process 사이에 자원과 변수들을 공유하지 않는다는 특징에 기인합니다. 

 

 Multi-threading이란 하나의 응용 프로그램을 수행하기 위하여 여러 Thread로 나누어 실행하는 방법으로, 각 Thread는 하나의 작업을 맡아 처리하게 됩니다. 즉 하나의 작업을 처리하기 위하여 여러 프로그램을 켜서 처리하는 것이 아니라 한 프로그램 내에서 그 작업들을 세분화해 처리한다는 개념입니다. Window와 Linux등 많은 OS에서 Multi thread를 지원하며 Web-server 환경은 대표적인 Multi thread 환경입니다. Multi thread의 경우 프로세스를 할당해 자원을 공유하는 System call이 줄어들고 Thread 간의 data 공간을 공유하기 때문에 자원 활용에 있어 많은 이점이 있습니다. 하지만 자원을 공유하는 특성 때문에 하나의 Thread에 문제가 생기면 전체 Process에 영향을 끼칠 수 있으며 Thread간의 자원 공유시 전역 변수를 사용하기 때문에 함께 사용시 충돌이 발생할 수 있다는 단점이 있습니다. 하지만 일반적으로 Process 간 통신 (IPC)의 처리 비용보다 Thread 간의 통신에 대한 처리 비용이 훨씬 적게 든다는 장점이 존재합니다. (Multi thread 환경에서는 Context switchig시 Stack 영역만 처리하면 됨) 

 

 

(3) GPU Context switching

 

 Context switching의 개념은 한정적인 자원인 Processor에서 여러 Process 및 Thread들을 효과적으로 처리하기 위한 방법으로 현재 진행하고 있는 Task(Process or Thread)의 상태를 저장하고 다음에 진행할 Task의 상태를 읽어들여 실행하는 방법입니다. Processor는 Context Switching 처리를 통해 현재 Task가 수행되는 동안 다음 Task를 대기시키는 것이 아니라 사용자에게 여러 Task가 동시에 처리되도록 보여지게 합니다. 하지만 Context switching에는 cost가 존재합니다. (일반적으로 Process의 Context switching이 Thread의 Context switching보다 큰 cost가 듭니다.) 

 

 GPU의 Context switching은 CPU에서 실행되는 것과 상황이 조금 다릅니다. CPU에서의 Context switching은 상당히 큰 cost가 드는 반면에, GPU에서는 비교적 자유로운 환경에서 실행되게 됩니다. 기본적으로 Processor 내부에서 Context switching이 발생할 때 Register 값을 새롭게 저장하거나 변경해야 합니다. GPU에서는 CPU보다 훨씬 많은 Register가 존재하기 때문에 Context switching에 대한 Overhead가 거의 발생하지 않습니다. 또한 GPU에서는 Warp 단위로 Context switching이 발생하는데 최대한 많은 Warp를 할당하여 Task 수행에 드는 Latency를 hiding하게 됩니다.

 

 

(4) MPI : Message Passing Interface

 

[Figure 10] Message Passing Interface

 

 Message Passing Interface란 병렬 처리에 사용되는 표준 라이브러리를 의미합니다. Message passing이란 지역적으로 구별된 메모리 공간을 갖는 process 들이 data들을 공유하기 위하여 메세지(공유할 data 들)를 송수신 하는 방법을 의미하며 Parallelism을 위한 여러 부분을 programmer가 직접 수행하게 되며 복잡하지만 유연한 적용이 가능하다는 장점이 있습니다. 즉 위의 그림처럼 하나의 프로세스를 n개의 프로세스로 쪼개어 병렬 처리한 후에 그 출력 값을 다시 모아 최종적인 결과 값으로 도출해내는 일련의 과정이 Message passing 기술로 수행되며, Message passing을 위한 라이브러리 중 하나가 바로 MPI 입니다.

 

MPI : 메세지 패싱 병렬 프로그래밍을 위해 표준화된 데이터 통신 라이브러리 

 


 

[5] Reference : 

(1) https://on-demand.gputechconf.com/gtc/2015/presentation/S5584-Priyanka-Sah.pdf

(2) Multi-Process Service :: GPU Deployment and Management Documentation (nvidia.com)

(3) https://levelup.gitconnected.com/diy-multithreading-vs-multiprocessing-in-python-fb93698ca7f3

(4) https://hayunjong83.tistory.com/17

(5) http://ap2.khu.ac.kr/download/mpi_lec.pdf

 

 

 

 

LIST