본문 바로가기
CUDA 프로그래밍

MPS란?

by 채소장사 2020. 1. 29.

  GPU를 HPC 연산 등에 활용하게 되면서, 높은 성능 향상을 체감하는 동시에 한정된 자원을 좀 더 효율적으로 사용하는 연구도 진행되고 있다. 이러한 resource under-utilization 연구는 elastic kernel, persistent thread, kernel fusion 과 같이 소프트웨어적인 해결책이 먼저 연구되었지만, 이제는 NVIDIA의 지원 아래 하드웨어적 특성을 고려한 해결방안 들이 제시되고 활용되고 있다. 스트림(stream), 하이퍼 큐(Hyper-Q)를 거쳐서 제시된 MPS의 개념 대하여 간단하게 살펴보려고 한다.


  MPS( Multi-Process Service)는 이름에서도 알 수 있듯이 다수의 프로세스가 동시에 단일 GPU에서 실행되도록 해주는 런타임 서비스이다. 간단하게 이전에 활용된 동시 실행 방식들을 비교해보면,

  스트림(stream)은 CUDA 실행을 위한 비동기 객체로서, 각 스트림에 속한 작업끼리는 순차실행이 강제되지만 서로 다른 스트림끼리는 동시 실행이 가능한 방법이다. 디바이스의 사용 자원이 중첩되지 않은 한에서 동시 실행이 되기 때문에, 보통 한 커널의 데이터 전송과 다른 커널의 실행이 서로 다른 스트림에서 중첩되는 방식으로 설명되고는 한다. 

  그런데 스트림을 이용하여 여러 작업을 서로 다른 태스크로 설정하더라도, 실제 GPU의 스케줄링 큐(=워크 큐)는 하나 였기 때문에 스케줄링 방법에 따라 서로 다른 스트림의 작업이 중첩될 수 있음에도, 한 스트림의 결과를 기다리는 작업에 의해 다른 스트림의 작업이 가로막히는 일이 발생할 수 있었다. 이러한 false dependency 문제를 해결하기 위해 제시된 방법이 하이퍼 큐(Hyper-Q)이다. 하이퍼 큐는 다수의 하드웨어 워크 큐(work queue)를 생성하는 Grid Management Unit를 통해 각 스트림이 같은 작업 파이프라인으로 유지되게 함으로써 종속성 문제가 없는 스케줄링이 가능하게 하였다.

  하이퍼 큐에 의해서 디바이스의 자원 활용률은 상당히 개선될 수 있었지만, 하이퍼 큐를 사용하려면 태스크 들이 반드시 같은 컨텍스트(context)에 속해야만 하는 제한이 있었다. 따라서 사실상 서로 다른 애플리케이션의 실행이 디바이스에서 중첩되는 것은 불가능하였다. MPS는 MPS 서버에 커널을 제출하는 방식으로 서로 다른 컨텍스트의 커널들이 단일 컨텍스트로 인식되게 하여 디바이스의 리소스를 최대한 활용할 수 있게 해준다.


  MPS의 대략적인 실행방식은 다음과 같다.

MPS 컨트롤 데몬(control daemon) 프로세스가 MPS 서버를 시작시킨다. MPS 서버에 의해 만들어진 실행 환경에 MPS 클라이언트가 커널을 제출한다. 이 실행환경은 공유 컨텍스트(shared context)로 동작할 수 있기 때문에 MPS 서버는 클라이언트의 요구사항을 대표하여 디바이스에 작업들을 동시에 실시한다.

  맥스웰부터 소개된 MPS는 볼타(Volta)를 기점으로 상당히 개선되었다. Volta 이전에는 각 클라이언트가 디바이스 주소공간을 공유되었지만, Volta부터는 MPS서버 대신에 직접 디바이스에 작업을 제출하도록 하고, 독립된 주소공간(isolated)을 제공한다. 또 QoS 제공을 위한 실행자원량을 제한할 수도 있다.

  이상의 차이점을 포함하여 MPS의 장점과 제한사항을 좀 더 살펴본다.


  MPS를 통한 공유 컨텍스트는 각 컨텍스트 마다 할당되는 디바이스의 데이터 저장공간이나 스케줄링 유닛 또한 공유될 수 있다는 것을 의미한다. 또한 서로 다른 프로세스가 GPU를 시분할방식으로 공유하기 위해서 발생하는 스케줄된 리소스의 스왑, 즉 컨텍스트 스위치 오버헤드가 줄어들게 된다. 결과적으로 MPS를 이용하여 자원 활용률을 향상시키고, 실행시간의 감소를 기대할 수 있다.

  NVIDIA의 MPS 공식문서 중에서 주목해야하는 제한점을 살펴보면 다음과 같다.

  • 다수의 MPS 클라이언트가 동시 실행되는 것과 달리, MPS 서버는 오직 한 유저만이 실행시킬 수 있다. 이는 다시 말해서 같은 UID로 서버에 제출된 클라이언트만이 GPU에서 동시 실행될 수 있다는 의미이다.
  • dynamic parallelism은 지원되지 않는다.
  • Volta 이전의 디바이스에서는 어떠한 Stream Callback도 사용할 수 없다.
  • MPS 클라이언트 중의 하나가 비정상적으로 종료된다면, MPS서버와 다른 클라이언트들이 어떤 상태에 있는지 알 수 없게 된다. 

이 때, 앞서 언급한 것처럼 디바이스 메모리 공간을 분리시켜주는 Volta GPU에 비하여, 이전 세대의 MPS는 클라이언트끼리 메모리 공간을 공유하기 때문에 Memory Protection과 Error Containment 등을 신경 써야한다.

  • Pre-Volta GPU는 동일한 가상 주소공간에서 MPS 클라이언트에게 메모리를 할당한다. 이 때문에 CUDA 커널이 할당된 메모리 이상으로 쓰기( out-of-range write)가 일어나 다른 프로세스의 메모리 상태를 변경하는 일이 발생할 수 있다. 그렇지만 어떠한 에러도 인지되지 못한다. 같은 이유로 CUDA 커널이 다른 프로세스가 수정한 메모리를 읽더라도 어떤 에러가 발생하는 지 알 수 없고, 결과적으로 커널의 동작을 예상할 수 없게 된다.
    이러한 동작은 따라서 CUDA 커널 안의 포인터로만 메모리 접근을 하도록 하여서 제한하여야 한다.
  • MPS 클라이언트는 스케줄링 유닛을 공유하는 것처럼 에러 보고 리소스(error reporting resource)도 공유한다. 따라서 한 클라이언트에 의해 발생된 예외는 모든 클라이언트에게 보고되지만, 이 때 어느 클라이언트가 에러를 발생시키는 지는 알 수 없다.
    또 한 클라이언트가 일으킨 에러가 프로세스를 종료시킨다면, 모든 GPU 클라이언트의 동작이 멈출 수 있다.

마지막으로 GPU 세대 간의 차이로 인해 비록 MPS가 멀티 GPU 시스템에서 사용될 수 있지만, 이 때 GPU들은 Volta GPU에서만 동작하거나 아니면 Pre-Volta GPU에서만 동작하는 식으로 분리된다.


 이상의 MPS의 대략적인 개념과 특징들을 바탕으로 resource under-utilization에 대해 고찰하기 위해서는 추가적으로 MPS Architecture와 MPS 사용방식에 대해 살펴볼 필요가 있다. 이러한 내용들은 추후에 새로운 글에서 다시 소개하려고 한다.

댓글