[GPGPU Series 5] Scheduling Thread Blocks

GPGPU Series 4에서 Application, Kernel (Grid), Thread Block (TB or CTA), Thread에 대한 설명을 작성하였다. GPGPU Series 3에서는 대략적인 GPU 구조에 대한 설명을 하였다. 이번 글에서는 Kernel의 TB가 Streaming Multiprocessor (SM)에 Scheduling되는 방법에 대한 설명을 진행할 예정이다.

이전에 작성한 글에서 각 Application은 1개 이상의 Kernel로 구성되어 있고, 각 Kernel은 다시 1개 이상의 TB로 구성되어 있다고 설명하였다. 보통 GPGPU Application을 실행하면 각 Kernel이 순차적으로 GPU에 할당되고, GPU는 각 Kernel 연산을 수행한다. 예를 들어서 Application이 2개의 Kernel로 구성되어 있으면,  첫 Kernel 연산을 수행 후 다음 Kernel 연산을 시작한다.

그림 1: Thread Block Scheduling 예제 

Kernel 연산이 시작되면 가장 먼저 Thread Block Scheduler (GigaThread Engine)이 TB Scheduling을 시작한다. TB Scheduling은 보통 Round-Robin 순서에 따라 Scheduling 한다. 예를 들어 GPU가 16개의 SM을 가지고 있고, Kernel이 160개의 TB로 구성되어 있다고 가정하자. 그럼 TB Scheduler는 TB0를 SM0 번에, TB1을 SM1 번의 순서대로 Scheduling 한다. 그림 1은 160개의 TB가 Scheduling 되는 방법을 묘사한 것이다. TB Scheduler는 SM이 최대한 수용할 수 있는 만큼 TB를 할당한다. 총 실행 가능한 TB의 개수는 보통 아래 3가지 요소로 인해서 제한된다.

  • SM의 총 실행가능한 Thread 개수
  • SM의 Register 개수
  • SM의 Shared Memory (Scratchpad Memory) 크기

TB의 Thread 개수는 프로그래머(사용자)가 결정한다. 이전 글에서 TB의 Thread 개수를 결정하는 부분에 관해 설명을 하였다. 만약 프로그래머가 TB 크기를 256개의 Thread로 설정하고, GPU의 SM이 총 2,048개의 Thread를 동시에 실행할 수 있다면 TB Scheduler는 각 SM에 총 8(2048/256)개의 TB를 Scheduling 한다.

Register 개수는 보통 컴파일 과정에 결정된다. CUDA로 작성된 Application을 컴파일하면 Thread 당 필요한 Register개수가 결정된다. 예를 들어 컴파일한 Application의 첫 Kernel의 Thread가 개당 20개의 Register를 사용한다고 가정하고 TB는 총 256개의 Thread로 구성되어 있다가 가정하자. 그럼 각 TB당 5120개의 Register가 필요하다. SM이 총 20000개의 Register를 가지고 있다면, TB Scheduler는 3개의 TB를 SM에 Scheduling 할 수 있다. 그럼 4,640(20000 – 5120 * 3)개의 Register는 어떻게 되는가? 사용하지 않고 남겨둔다. SM에 Scheduling 되는 최소 단위는 TB이기 때문에 TB 1개를 Scheduling 할 수 없으면 더 이상 TB를 Scheduling 할 수 없다. 만약 Thread 당 Register 사용 개수를 제안하여 1개의 TB를 더 Scheduling 하고 싶으면 컴파일 과정에 Thread 당 Register 사용량을 제한해야 한다. 또는 프로그램을 다시 작성해야 한다. 하지만 적은 Register양을 사용하게 되면 Memory 접근 횟수가 증가하기 때문에 오히려 Application 전체 성능이 하락하기도 한다.

다음은 Shared Memory 크기 제한이다. CUDA 프로그래밍하게 되면 Shared Memory라는 Programmable 가능한 메모리를 사용할 수 있다. Shared Memory 사용량은 TB 단위로 결정된다. 이 부분은 컴파일 과정에 결정되는 요소가 아니라 프로그래머가 TB당 사용하는 Programmable 메모리 크기를 결정하게 된다. Register와 같이 SM에 총 Scheduling 되는 TB의 Shared Memory 사용량이 SM의 Shared Memory 크기를 넘어갈 수 없다.

TB Scheduler가 TB를 Scheduling 할 때 위의 3가지 요소를 고려한다. 위의 3가지 요소 중 1개만이라도 부족하면 TB Scheduler는 TB를 SM에 Scheduling 하지 못한다. 다시 말해서 코딩을 하는 과정에서 위 3가지 요소를 충분히 고려해야 GPU를 효율적으로 사용할 수 있다.

마지막으로 SM에 실행되지 않은 TB를 Scheduling 하는 방법이다. 보통 SM에 Scheduling 된 TB는 TB의 모든 Thread의 연산이 완료되면 사용하고 있던 Register, Shared Memory, Scheduling Resource를 Free하고 종료된다. 이렇게 연산이 종료되면 TB Scheduler는 다음 TB를 해당 SM에 Scheduling 하게 된다. 다시 말해서 TB Scheduler는 SM에 Register, Shared Memory, Scheduling Resource가 남으면 새로운 TB를 Scheduling 한다. 만약 동일 시간에 2개의 다른 SM의 TB가 종료되면 TB Scheduler는 TB를 Round-Robin 순서대로 Scheduling 한다.

Leave a Comment