JD의 블로그

[CUDA] MPS(Multi-Process Service)란? 본문

프로그래밍/운영체제

[CUDA] MPS(Multi-Process Service)란?

GDong 2021. 10. 15. 09:18

GPU를 HPC 연산 등에 활용하면서 높은 성능 향상을 체감하는 동시에 한정된 자원을 좀 더 효율적으로 사용하는 연구도 진행중이다. 

이전에는 소프트웨어적인 해결책이 연구되었지만, NVIDIA의 지원 아래 하드웨어적 특성을 고려한 해결방안들이 제시되고 있다.

 

스트림(Stream), 하이퍼 큐(Hyper-Q)를 거쳐서 제시된 MPS의 개념에 대해서 간단히 살펴보자.

 

MPS란?

MPS(Multi-Process Service)는 다수의 프로세스가 동시에 단일 GPU에서 실행되도록 해주는 런타임 서비스다. 

 

이전에 활용된 동시 실행 방식을 살펴보면

  • 스트림(Stream)
    • CUDA 실행을 위한 비동기 객체로, 각 스트림에 속한 작업끼리 순차실행이 강제되지만 서로 다른 스트림끼리는 동시 실행이 가능한 방법이다. 디바이스의 사용 자원이 중첩되지 않는 한에서 동시 실행이 되기 때문에 보통 한 커널의 데이터 전송과 다른 커널의 실행이 서로 다른 스트림에서 중첩되는 방식으로 설명되곤 한다.
    • 스트림을 이용하여 여러 작업을 서로 다른 태스크로 설정하더라도, 실제 GPU의 스케줄링 큐(워크 큐)는 하나였기 때문에 스케쥴링 방법에 따라 서로 다른 스트림의 작업이 중첩될 수 있음에도 한 스트림의 결과를 기다리는 작업에 의해 다른 스트림의 작업이 가로막히는 일이 발생할 수 있었다. 
    • 이러한 false dependency 문제를 해결하기 위해 제시된 방법이 하이퍼 큐(Hyper-Q)이다.
  • 하이퍼 큐(Hyper-Q)
    • 다수의 하드웨어 워크 큐(work queue)를 생성하는 Grid Management Unit을 통해 각 스트림이 같은 작업 파이프라인으로 유지되게 함으로써 종속성 문제가 없는 스케줄링을 가능하게 하였다. 
    • 하이퍼 큐에 의해 디바이스의 자원 활용률은 상당히 개선될 수 있었지만 하이퍼 큐를 사용하려면 태스크들이 반드시 같은 컨텍스트(context)에 속해야만 하는 제한이 있었다. 따라서 사실상 서로 다른 애플리케이션의 실행이 디바이스에서 중첩되는 것은 불가능했다. 
  • MPS(Multi-Process Service)
    • MPS는 서버에 커널을 제출하는 방식으로 서로 다른 컨텍스트의 커널들이 단일 컨택스트로 인식되게 하여 디바이스의 리소스를 최대한 활용할 수 있게 해준다.
    • 실행방식은 MPS 컨트롤 데몬 프로세스가 MPS 서버를 시작시킨다. MPS 서버에 의해 만들어진 실행 환경에 MPS 클라이언트가 커널을 제출한다. 이 실행환경은 공유 컨텍스트로 동작할 수 있기 때문에 MPS 서버는 클라이언트의 요구사항을 대표하여 디바이스에 작업들을 동시에 실시한다. 
    • MPS를 통한 공유 컨텍스트는 각 컨텍스트 마다 할당되는 디바이스의 데이터 저장 공간이나 스케줄링 유닛 또한 공유될 수 있다는 것을 의미한다. 또한 서로 다른 프로세스가 GPU를 시분할방식으로 공유하기 위해서 발생하는 스케줄된 리소스의 스왑, 즉 컨텍스트 스위치 오버헤드가 줄어들게 된다. 결과적으로 MPS를 이용하여 자원 활용률을 향상시키고, 실행 시간의 감소를 기대할 수 있다. 
    • 제한점
      • 다수의 MPS 클라이언트가 동시 실행되는 것과 달리, MPS 서버는 오직 한 유저만이 실행시킬 수 있다. 이는 다시 말해서 같은 UID로 서버에 제출된 클라이언트만이 GPU에서 동시 실행될 수 있다.
      • Dynamic parellelism은 지원되지 않는다.
      • MPS 클라이언트 중 하나가 비정상적으로 종료되면, MPS 서버와 다른 클라이언트들이 어떤 상태에 있는지 알 수 없게 된다.

 

MPS 활용 

쿠버네티스 위에서 AI/ML 워크로드를 수행하거나 GPU를 사용하는 경우

Linux에서 네임스페이스와 cgroup을 사용하여 쿠버네티스는 해당 여유 리소스가 있는 노드에서 필요한 CPU 및 메모리가 있는 파드를 예약한 다음 컨테이너를 사용하여 컨테이너에서 실행할 프로세스에 대한 CPU 및 메모리 할당량을 선택할 수 있다. 그러나 현재 쿠버네티스에는 GPU 리소스 관리 및 격리에 대한 내부 지원이 없기에 장치 플러그인 및 스케줄러를 확장하는 타사 솔루션을 이용할 수 있다. 

 

Nvidia Kubernetes Device Plugin은 쿠버네티스에서 Nvidia GPU를 사용할 때 일반적으로 사용되는 장치 플러그인이다. Nvida Kubernetes 장치 플러그인은 기본 GPU 리소스 할당 및 스케줄링, 각 작업자 노드에 대해 다중 GPU를 지원하고 기본 GPU 상태 확인 메커니즘을 가지고 있다. 그러나 파드 매니페스트에서 요청한 GPU 리소스는 정수만 가능하다. 즉 여러 포드 간에 단일 GPU 공유를 지원하지 않으므로 GPU 리소스를 충분히 활용하지 못할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: cuda-container
      image: nvidia/cuda:9.0-devel
      resources:
        limits:
          nvidia.com/gpu: 2 # cannot set fractional number

또 다른 문제는 여러 컨테이너가 하나의 GPU를 공유할 때 GPU 리소스 격리에서 발생한다. 기본적으로 여러 컨테이너/프로세스는 동시에 하나의 GPU에 있을 수 있지만 병렬로 있을 수는 없다. 타임 슬라이스 기반 다중 작업 스케줄링은 전체 성능에도 영향을 미친다. GPU 메모리 격리도 기본적으로 적용되지 않는다. 애플리케이션에서 일반적으로 리소스 관리 방식으로 자체 GPU 메모리 제한을 설정해야하는데 Tensorflow에는 per_process_gpu_memory_fraction 을 통해 각 프로세스에 할당할 사용 가능한 GPU 메모리 비율을 설정하는 매개변수가 있다.

 

각 파드에 대한 GPU 리소스의 소수 설정에 대한 제한을 해결하기 위한 방법은 쿠버네티스 장치 플러그인에서 각 GPU 리소스에 대한 가상 단위를 설정하는 것이다. 예를 들어 각 물리적 GPU에 대해 10개의 가상 GPU를 할당할 수 있는 반면 파드는 1/10 물리적 GPU 리소스를 나타내는 1 GPU 단위를 요청한다. 리소스 할당은 쿠버네티스 스케줄러에서만 발생한다. CPU 및 메모리 할당과 달리 컨테이너에 GPU 사용에 대한 제약은 없다. 기본적으로 GPU는 여러 컨테이너가 하나의 GPU를 공유하는 동안 리소스 격리를 지원하지 않는다. 

 

Nvida는 CUDA 호환 API용 MPS를 제공하여 병렬로 실행되는 애플리케이션의 리소스 활용도도 향상시켰다. 

 

AWS에서는 Virtual GPU device plugin for kubernetes 를 통해서 이러한 문제를 해결하도록 GPU 장치 플러그인을 설계하고 제공하고 있다. 쿠버네티스를 사용하여 GPU를 사용하는 것은 쉽지 않은데 쿠버네티스 장치 플러그인과 Nvidia MPS를 사용하여 GPU 리소스 활용도를 크게 높이고 비용을 절감할 수 있다.

 

 

Reference

NVIDIA - MPS

MPS란?

Virtual GPU Device Plugin for Inference workload in Kubernetes