본문 바로가기

CUDA C

[ CUDA 기반 GPU 병렬처리 프로그래밍 ] chapter 1 GPGPU 및 병렬처리 개요

이 글은 CUDA 기반 GPU 병럴처리 프로그래밍 책의 내용을 바탕으로 정리한 글이다.

 

글을 정리하기에 앞서, CUDA_C 와 관련하여 기초적인 문법이나, 활용 등은 할 수 있다. 그럼에도 하나의 책을 잡고 다시 정리하는 데는, 현재 내 수준이 기초부터 탄탄하게 잡아야할 필요성을 느꼈기 때문이다. 설명이라기 보다는 내가 이해한 바를 적고자한다.


병렬 처리의 개념

  병렬 처리는 영어로 parallel processing을 말합니다. 문제를 어떻게 해결한다는 말이냐 하면 하나의 문제를 여러 개로 나누고 그 각각의 문제를 하나의 연산 유닛이 감당해서 처리한다는 말이다. 간단한 예시를 들어보면,

 

  예시) 1-15 까지 더하는 문제 

  가정 - 내 하드웨어는 5개의 연산 유닛을 가지고 있음

  1. 15 개의 숫자를 5개의 그룹으로 나눔 ( 1-3, 4-6, ... , 13-15 )

  2. 각각의 유닛이 각 그룹에 대해 덧셈을 진행

  3. 해당 유닛들의 결과를 최종적으로 모아서 더함

 

이 예시가 병렬 처리의 기본 예시이다. 즉 ' 큰 문제를 여러 개로 나누고 각각의 core가 해당 일을 계산한다 '가 핵심이다. 그럼 병렬 처리 프로그램의 키포인트는 무엇일까? 연산 유닛이 놀지 않도록 최대한 활용하는 것이 핵심이라 할 수 있겠다. 가지고 있는 여러 core가 놀지 않고 계속 일하게 만드는 것이 효율적인 병렬 처리라 할 수 있겠다.


병렬 처리 장치

  병렬 처리 장치에 대해 알아야 하는 이유는 장치에 따라 병렬 처리 전략이 달라지기 때문이다. 플린의 분류법에 따르면, 컴퓨팅 하드웨어를 4 가지의 종류로 나눈다. SISD, SIMD, MISD, MIMD 가 그 분류이다. 분류 기준은, 데이터에 대해 한번에 수행하는 명령어의 개수와 명령어가 수행되는 데이터의 개수다.

 

  SISD는 하나의 명령어를 하나의 데이터에 대해 수행한다. MISD는 여러 개의 명령어를 하나의 데이터에 실행하며, MIMD 는 여러개의 명령어를 여러 개의 데이터에 수행한다. SIMD는 하나의 명령어를 여려 개의 데이터에 실행한다. 이 중 우리가 알아야할 GPU는 SIMD와 유사하지만 제어 단위가 스레드이다. 이는 뒤에서 더 다루도록 하겠다.

 

  실생활 예시를 들어 설명을 덧붙이면, 단일 코어 CPU 시스템이 SISD이고, 우리가 흔히 사용하는 멀티 코어 CPU의 경우 MIMD 이며, SIMD의 구조는 GPU를 예로 들 수 있겠다. MISD 구조의 아키텍쳐는 실존하지 않는다. 이 때, MIMD는 task-level parallelism이라고 표현하고, SIMD는 data-level parallesim이라고 한다.

 

  MIMD 예시 ) 1-3 으로 이루어진 data1, 4-6으로 이루어진 data2, 7-9로 이루어진 data3

  1. data1에는 덧셈을, data2에는 곱셈을, data3은 제곱 후 덧셈을 수행하는 컴퓨터 구조

  SIMD 예시 ) 1-3으로 이루어진 data1

  가정 - 3개의 코어( 산술 유닛 ) 이 있다고 가정

  1. SISD 를 활용하면 3 번의 덧셈 3번, 반복적으로 이뤄짐

  2. SIMD 에서는 3 개의 core가 있음으로 3 번의 덧셈이 한번에 이뤄짐 

 

    병렬 처리 하드웨어의 다른 기준 중에 하나는 메모리 공유 여부이며, 이에 따라 공유 메모리 시스템과, 분산 메모리 시스템으로 나눠진다. 공유 메모리 시스템에서는 여러 스레드가 같은 메모리 공간에 접근할 수 있다는 것이 특징이며, 여러 개의 스레드가 접근할 경우 서로의 작업에 순서 등을 맞춰주는 과정을 synchronization이라고 한다. 분산 메모리 시스템은 각 연산 장치가 독립된 메모리 공간은 갖는 시스템이다. 분산 메모리 시스템에서 네트워크 등을 통해 데이터를 주고 받아야 한다. 예시로는 PC가 예시이다. 우리가 코딩을 하면 CPU에서 시작하고, 병렬 처리 해야할 데이터를 GPU로 옮겨주어야 한다. 즉 CPU와 GPU 각각은 공유 메모리 시스템이지만 이를 포함하는 PC는 분산 메모리 시스템이다.


CPU와 GPU 비교

  GPU의 병렬 처리 특성을 이해하는데 필요한 것은 CPU의 탄생 배경과 발전 경향이다. 그래서 비교를 통해 알아보려고 한다.  우선 책에 나와있는 예시 먼저 정리하겠다. 책에서는 카메라에서 사진 찍는 예시를 든다.

 

  카메라에서 사진을 찍는 예시

  1. 물체에 반사된 빛이 카메라로 들어온다.

  2. 카메라 내부에서는 각 픽셀 단위로 들어온 빛의 색이 달라진다.( 이 과정을 반대로, 광선을 쏘아 만나는 물체 일부의 색상 값이 픽셀 값이 된다. )

  3. 픽셀의 색상 값은 카메라의 위치, 각도, 빛의 세기 및 방향에 따라 달라진다.

  4. 즉 각 픽셀의 값은 픽셀에 projection되는 표면 정보, 카메라의 빛의 정보를 알면 알 수 있다.

  

  이 예시가 이해가 잘 되지 않았다. 픽셀의 색상 값을 결정하는 작업이 픽셀 독립적으로 처리가 가능하다고 하는 데, 위의 예시만으로는 와닿지가 않았다. 다시 한번 정리해 보면, 결국 하려는 일은 3차원 공간을 픽셀에 투영 시키는 작업이다. 이는 공간을 나타내기 위해 필요한 것은 원하는 픽셀에 원하는 색상을 넣어주면 된다는 말과 같다. 원하는 색상은 다른 말로 원하는 픽셀 값과 같다. 원하는 색상은 카메라의 빛의 정보를 통해 판단할 수 있다. 돌아가서, 원하는 픽셀이라는 말은 projection될 위치와 같은 말이다. 이는 projection 되는 표면 정보에 해당하는 말이다. 그럼 어떤 색을 어디에 넣어주면 되는지 알면 우리는 3차원 공간을 픽셀로 이루어진 2차원에 옮길 수 있다는 말을 위의 예시로 설명한 것이며, 이는 픽셀별로 독립적이기 때문에 병렬처리가 가능한다는 예시를 든 것이라 할 수 있다.

 

  GPU의 탄생 배경 

  GPU의 탄생 배경은 3차원 공간의 복잡도가 증가하면서, 그래픽스 파이프라인을 CPU로 처리하는데 한계가 발생했고, 이를 효율적으로 처리하기 위해 탄생하게 되었다. GPU는 픽셀의 색상 값을 독립적으로 병렬 처리할 수 있도록 개발되었다. 각 픽셀의 색상 값 계산에는 많은 연산이 필요하지 않는 점에 주목하여, 낮은 성능의 코어를 가능한 많이 집적하는 SIMD 형태로 발전하게 된다. 여기서 병렬 처리 능력에 주목을 받게 되었고, 이 관심이 현재 프로그래밍이 가능한 GPU의 등장으로 이어지고, 지금의 GPGPU가 정립되게 된다.

 

  CPU vs GPU

    멀티 코어 CPU

  구조는 아래와 같다.

그림 직접 그림 ,   원본 그림 출처 : 엔비디아 CUDA C 프로그래밍 가이드

cpu는 범용성 연산 장치로, 다양한 연산에 일반적으로 작동하도록 발전해 왔다. 클럭 주파수를 높여 연산 코어의 성능을 높이는 방식으로 발전해 왔다. 따라서 단일 코어 성능은 GPU의 연산 코어보다 뛰어나다. 프로그램은 많은 분기를 가지고 있음으로, 메모리 접근 역시 불규칙한 경우가 많기 때문에, 불규칙한 동작에 따른 성능 저하를 줄이기 위해 CPU는 branch prediction 기법과 불규칙한 메모리 접근에 병목현상을 해소할 수 있는 캐시 효율 향상에 많은 투자를 해왔다. 이 둘은 잘하기 위해서는 프로그램의 흐름을 잘 제어하고 있음을 뜻하고, 이는 control unit과 DRAM 및 캐시가 CPU에서 중요한 역할을 차지함을 뜻한다.

 

  반면, GPU는 수백에서 수천개의 코어를 가지고 있다. 따라서, FLOPS 측면에서의 CPU의 연산 능력 ( 초당 처리량 )은 GPU에 비해 낮다. 그렇지만 CPU의 단일 코어 성능이 GPU의 단일 코어에 비해 월등하다는 사실과 제어 및 메모리 접근 효율성에 강점이 있기 때문에, 불규칙한 흐름을 가지는 연산에 대해서는 GPU에 비해 높은 성능을 보여준다는 사실은 꼭 기억하고 넘어가야한다.

  GPU

  GPU의 발전 배경에서 봤듯이, GPU는 그래픽스 파이프라인 연산 가속을 위해 개발되었으며, 가시화 파이프라인 연산을 동시에 처리하기 위해 코어 수를 늘려왔다. 주어진 공간에 많은 수의 코어를 넣기 위해, 개별 코어의 성능을 높이기보다 각 픽셀을 처리하는 수준의 성능을 유지하면서 꼭 필요한 제어 기능만 넣어 크기를 줄이는 방향으로 발전했다. 

 

  일반적으로, 각 픽셀의 색상 값 계산을 위한 연산은 같다. 즉 같은 종류의 연산을 서로 다른 데이터에 적용한다. 따라서 하나의 제어장치가 여러 개의 연산 코어를 제어하는 구조를 가진다. 또한 그래픽스 파이프라인 연산의 흐름이 규칙적이기 때문에 메모리에 대한 접근 형태 역시 규칙적이다. 따라서 캐시에 상대적으로 적은 비중을 부여한다. 그렇지만, 많은 수의 코어에 데이터를 동시에 공급해야 함으로 한 번에 많은 데이터에 접근해야 한다. 따라서 높은 대역폭을 갖는 고성능 메모리가 필요하다. 

 

  많은 수의 연산 코어를 GPU는 CPU에 비해 FLOPS 관점에서 높은 처리 성능을 가진다. 그렇지만 전제 조건이 붙는데, 모든 연산 및 메모리 접근이 규칙적이어야한다. GPU는 제어장치 및 캐시에 적은 공간을 할애함으로, 스레드 사이의 불규칙한 연산 흐름 및 임의적인 메모리 접근 형태에 취약하다. 한 스레드 그룹 내 if 문 등의 분기가 있으면 다른 분기를 따르는 스레드의 동작은 직렬화 되며, 병렬 처리 성능이 크게 떨어진다. 이와 마찬가지로 불규칙한 메모리 접근 역시 성능을 저하시킨다.

 


참조 : CUDA 기반 GPU 병렬 처리 프로그래밍