GPGPU

GPGPU(, GPU 상의 범용 계산)는 일반적으로 컴퓨터 그래픽스를 위한 계산만 맡았던 그래픽 처리 장치(GPU)를, 전통적으로 중앙 처리 장치(CPU)가 맡았던 응용 프로그램들의 계산에 사용하는 기술이다. 이를 가능하게 한 것은 프로그램 가능한 층과 고정도 연산을 그래픽 파이프라인에 연결하는 것으로, 이를 통하여 소프트웨어 개발자들이 그래픽이 아닌 데이터에 스트림 프로세싱을 사용할 수 있게 된다.

GPU 개선 
GPU 기능은 전통적으로 매우 제한적이었다. 사실 여러 해 동안 GPU는 단지 그래픽스 파이프라인의 특정 부분을 가속시키기 위해서만 사용되었다. GPGPU가 가능하려면 몇 가지 개선이 필요했다.

가프로그램성 
그래픽스 파이프라인의 일부에 추가된 프로그램 가능성 덕분에 게임 프로그래머는 더 실감나는 효과를 생성할 수 있게 되었다. 버텍스 세이더와 프래그먼트 셰이더가 대표적인 그래픽스 파이프라인의 프로그램 가능한 부분이며 비교적 최근에 추가된 기하 셰이더 또한 그 활용 사례가 늘고 있다. 버텍스 셰이더로 프로그래머는 각각의 버텍스(모델링 폴리곤의 꼭지점) 특징, 즉, 위치, 색상, 패턴 좌표, 그리고 수직 벡터를 다르게 할 수 있게 되었다. 프래그먼트 셰이더는 프래그먼트, 또는 각 픽셀의 색상을 계산하는데 사용된다. 프로그램 가능한 프래그먼트 셰이더로 프로그래머는, 예를 들어, 그래픽 카드가 기본으로 제공하지 않는 조명 모델을 사용할 수 있게 된다. 전형적인 예는 간단한 가우드 셰이딩이다. 셰이더로 그래픽스 프로그래머는 렌즈 효과, 변위 매핑, 그리고 필드 깊이를 창조할 수 있게 되었다.

셰이더 모델의 가프로그램성과 역량은 버전에 따라 점차로 증대되어 DirectX 표준을 따르는 하드웨어가 계속 따라오게끔 하였다. DirectX 10 규격의 셰이더 모델 4.0은 버텍스(vertex,), 지오메트리(Geometry, 기하적 형상), 그리고 프래그먼트 처리의 프로그래밍 규격을 통일하여 프로그램을 실행시킬 수 있는 단일한 계산 자원을 제공한다.

자료형 
DirectX 9 이전의 그래픽 카드는 팔레트 또는 정수 색만 지원하였다. RGB 요소를 담은 다양한 형태가 가능하다. 때로 알파 값이 추가되어 투명도에 사용되었다. 흔히 사용된 형태는 다음과 같다:

초기에 확정된 기능 또는 가프로그램성이 제한적이었던 그래픽스(DirectX 8.1 호환 GPU 까지)에서는 이로써 충분하였다. 이는 또한 화면에 사용된 표현 방식이기도 했기 때문이다. 이 표현 방식은 그러나 어떤 제한 조건을 가지고 있다. 그래픽 프로세서 성능이 충분하다면 그래픽 프로그래머도 더 나은 형식을 사용하고 싶어할 것이다. 예를 들어 부동소수점 자료형식으로 고 동적 범위 이미지효과를 얻을 수 있기 때문이다. 수많은 GPGPU 응용 프로그램은 부동소수점 정밀도를 요구하며 이는 DirectX 9 규격을 따르는 그래픽 카드부터 적용되었다.

DirextX 9의 셰이더 모델 2.x은 전 정밀도와 부분 정밀도의 두가지 정밀 변수형 지원을 제안하였다. 완전 정도는 FP32 아니면 FP24(요소당 24비트의 부동소수점) 또는 그 이상을 지원하는 것이고, 부분 정밀도는 FP 16이었다. ATI의 R300 시리즈 GPU는 FP24 정도를 프로그램 가능한 프래그먼트 파이프라인에서만 지원하였다. 꼭짓점 프로세서에서는 FP32를 지원하였다. 반면 엔비디아의 NV30 시리즈는 FP16과 FP32를 지원하였다. 다른 공급 업체, 즉, S3와 XGI는 혼합 형식을 FP24까지 지원하였다.

셰이더 모델 3.0은 규격을 변경하여 전 정밀도 요구 조건을 상향조정하여 최소한 FP32를 프래그먼트 파이프라인에서 지원하게 하였다. ATI의 셰이더 모델 3.0 준수 R5xx 세대 (라데온 X1000 시리즈)는 FP32를 파이프라인에 걸쳐 지원하지만 엔비디아의 NV4x와 G7x 시리즈는 FP32 전 정밀도와 FP16 부분 정밀도 모두를 계속 지원하였다. 셰이더 모델 3.0에서 요구되지는 않았지만 ATI와 엔비디아의 셰이더 모델 3.0 GPU는 혼합 FP16 렌더 타겟 지원을 소개하여 더 쉽게 고 동적 범위 렌더링을 지원할 수 있게 하였다.

엔비디아 GPU 상의 부동소수점 구현은 거의 IEEE 표준을 따른다. 그러나 모든 공급업체가 그런 것은 아니다. 이로 말미암아 어떤 과학적 응용 분야에 중요한 정확도 문제가 생긴다. 64비트 부동소수점 (배정밀도 부동소수점) 값은 CPU에서는 일반적이지만 GPU 중에는 지원하지 않는 경우도 있다. 어떤 GPU 구조는 IEEE 표준을 벗어나고 어떤 경우는 아예 배정밀도 자체를 지원하지 않는다. GPU에서 배정밀도 부동소수점 값을 모사하려는 노력이 있어왔지만 속도 손실로 말미암아 애초에 계산을 GPU상으로 옮겨서 얻어지는 이득이 사라진다.

GPU 상에서 이루어지는 연산 대부분은 벡터화된 형태로 이루어진다. 한가지 연산이 최대 4개 값에 대해 이루어진다. 예를 들어 한가지 색 가 다른 색상  로 모듈레이트된다면 GPU는 결과 색상인 를 한 연산으로 계산할 수 있다. 이 기능은 그래픽스에서 유용한데 그 까닭은 거의 모든 기본 자료형이 (2, 3, 4차원) 벡터이기 때문이다. (예: 꼭짓점, 색상, 수직 벡터, 표면 패턴 좌표 등) 많은 다른 응용 프로그램에서 이를 유용하게 사용될 수 있고, 벡터 명령 (SIMD)이 그 우수한 성능으로 CPU상에서 지원되었다.

2006년 11월 엔비디아는 쿠다를 발표하였는데, 이는 SDK와 API로 프로그래머가 C언어로 알고리즘을 기술하여 지포스 8 시리즈 GPU 상에서 실행시킬 수 있게 해 주는 것이다. AMD는 유사한 SDK를 ATI 기반 GPU와 스트림 SDK(전에는 CTM 클로우즈 투 메탈)라고 부르는 SDK와 기술을 제공한다. 이는 엔비디아의 쿠다와 직접 경쟁을 위해 설계되었다. CTM은 얇은 하드웨어 인터페이스를 제공한다. AMD는 또한 AMD 파이어스트림 제품군도 발표하였다. 이는 CPU와 GPU 기술을 한 칩에 모은 것이다. 전통적인 부동소수점 가속기, 예를 들어 64비트 클리어 스피드사 CSX700 보드는 오늘날의 수퍼컴퓨터에서 사용되는데, 현재 엔비디아와 AMD에서 나오는 최고급 GPU는 단정밀도 (32비트)를 강조하고 있다; 배정밀도 (64비트)는 훨씬 느리게 작동한다.

GPGPU 프로그래밍 개념 
GPU는 3차원 컴퓨터 그래픽스의 가속에 설계의 목적을 두고 있다. 때문에 매우 제한적인 경우에 한하여 일반적인 연산에 GPU를 사용할 수 있다. 대표적으로 GPU는 흐름 처리를 이용하여 풀 수 있는 문제에 효과적이며 최근에는 기술의 발전 덕분에 처리 가능한 문제의 종류가 늘고 있다. 그러나 그래픽카드 제조사와 관련 분야 연구자들의 노력에도 불구하고 GPU의 활용 영역과 방식은 CPU와 비교하면 아직 제한적이다.

흐름 처리 
GPU는 독립적인 꼭짓점들과 프래그먼트만 처리할 수 있지만 다수를 병렬로 처리할 수 있다. 이것은 특히 프로그래머가 같은 방식으로 많은 수의 꼭짓점 또는 프래그먼트를 처리하고자 할 때 유용하다. 이런 의미에서, GPU는 흐름 프로세서이다. 즉, 병렬로 한번에 하나의 커널을 흐름 속의 많은 레코드에 실행시키는 것이다.
흐름이란 단순히 유사한 계산을 필요로 하는 레코드의 모음이다. 흐름으로 데이터 병렬성을 구할 수 있다. 커널이란 함수로써 흐름 속의 각 요소에 적용되는 것이다. GPU에서는 꼭짓점과 프래그먼트가 흐름 속의 요소이고, 셰이더가 그 위에서 작동하는 커널이다. GPU가 요소들을 독립적으로 처리하므로 공유되는 또는 정적인 데이터는 없다. 단지 각 요소를 입력으로부터 읽고, 연산을 수행하고, 출력으로 쓸 뿐이다. 다양한 입력과 다양한 출력을 갖는 것은 허용되는 편이나 읽고 쓰기에 모두 사용되는 메모리는 없다.
산술 치열도는 전송되는 메모리 워드 당 연산으로 정의된다. 중요한 것은 GPGPU 응용 프로그램이 높은 산술치열도를 가지는 것으로 그렇지 않다면 메모리 접근 불확실성이 계산 속도를 제한할 것이다.
이상적인 GPGPU 응용 프로그램의 데이터 집합은 크고, 병렬도는 높고, 데이터 요소간 의존성은 최소이다.

GPU 프로그래밍 개념

계산 자원 
GPU가 사용 가능한 계산 자원은 다양하다:
 프로그램 가능한 프로세서 – 버텍스(꼭짓점), Primitive( 프리미티브, 3차원 그래픽스의 기본 요소, 점,선,면, 구, 상자) 프래그먼트 파이프라인으로 프로그래머는 데이터의 흐름에 커널을 배풀 수 있다.
 비트맵 변환기 – 프래그먼트를 만들고 꼭짓점 당 상수-예를 들어 텍스처 좌표와 색상-를 내삽한다
 텍스처유닛 – 읽기 전용 메모리 계면
 프레임 버퍼 – 쓰기 전용 메모리 계면
사실, 프로그래머는 프레임 버퍼 대신 쓰기 전용 텍스처로 바꿔치기할 수도 있다. 이는 텍스처로 그리기 Render-To-Texture (RTT) 또는 백버퍼로 그린 후 텍스처로 베끼기 Render-To-Backbuffer-Copy-To-Texture(RTBCTT) 아니면 더 최근의 흐름 출력으로 이루어진다.

흐름으로서의 텍스처 
GPGPU에서 흐름이 취하는 가장 일반적인 형태는 2차원 격자이다. GPU안에 만들어져 있는 랜더링 모델에 알맞기 때문이다. 다수의 계산이 격자 형태로 변환될 수 있다: 행렬 계산, 이미지 처리, 물리 기반 시뮬레이션 등. 텍스처가 메모리로 사용되므로 텍스처 룩업은 메모리 읽기로 사용된다. 이 덕분으로 어떤 연산은 GPU에 의해 자동으로 이루어진다.

커널 
커널은 반복문의 본체라고 생각할 수 있다. 예를 들어 만일 프로그래머가 CPU로 격자행렬을 다룬다면 그 코드는 다음과 비슷할 것이다:
// 입출력 격자는 각각 10000 x 10000를 가지고 있다.
void transform_10k_by_10k_grid(float in[10000][10000], float out[10000][10000])
{
   for(int x = 0; x < 10000; x++)
   {
     for(int y = 0; y < 10000; y++)
     {       // 다음 행이 1억 번 실행된다.
       out[x][y] = do_some_hard_work( in[x][y] );
     }
   }
 }
GPU상에서 프로그래머가 지정하는 것은 반복문의 본체 부분 (커널이 된다) 과 반복문이 기하 처리할 데이터 뿐이다.

흐름 제어 (flow control) 
순차 코드에서는 if-then-else문과 다양한 반복문으로 흐름을 제어하는 것이 가능하다. 최근에야 비로소 이러한 흐름 제어 구조가 GPU에 추가되었다. 조건부 쓰기는 일련의 더 간단한 명령을 쓰는 것으로 이루어질 수 있었지만, 반복문이나 조건부 분기는 불가능했다.
최근의 GPU는 분기를 허용하지만, 보통 성능상 손실을 감수해야 한다. 분기를 일반적으로 안쪽 반복문에서는 피해야 하는 것은 CPU나 GPU나 마찬가지이고, 다양한 기술, 즉, 정적 분기 해소, 선계산, Z-cull로 하드웨어 지원이 없을 때 분기할 수 있다.

GPU 기법

변환 (map) 
변환 연산은 주어진 함수 (커널)을 스트림의 모든 요소에 적용한다. 간단한 예로 스트림의 모든 값에 어떤 상수를 곱하는 것이다. (밝기 조절) 변환 연산은 GPU상에서 간단히 구현된다. 프로그래머는 화면의 각 픽셀의 프래그먼트를 생성하고 각각에 프래그먼트 프로그램을 적용한다. 결과로 얻어지는 같은 크기의 스트림이 출력 버퍼에 저장된다.

감축 
어떤 계산은 큰 흐름으로부터 작은 흐름(단 한개의 요소만 남을 수도 있다)을 계산해야 한다. 이를 흐름의 감축이라 부른다. 일반적으로 감축은 여러 단계로 이루어질 수 있다. 이전 단계의 결과가 이번 단계의 입력으로 사용되고, 연산이 적용되는 범위가 흐름 요소 하나만 남을 때까지 반복된다.

흐름 필터링 
흐름 필터링은 본질적으로 불균일 감축이다. 필터링은 어떤 기준에 따라 흐름에서 일부 요소를 제거하는 것이다.

흩뿌리기 
흩뿌리기 연산은 꼭짓점 처리에서 가장 자연스럽게 정의된다. 꼭짓점 처리는 꼭짓점의 위치를 조정할 수 있어서 프로그래머가 정보를 격자의 어디에 예치하는지 제어할 수 있게 해 준다. 다른 확장도 가능한데, 예를 들어 꼭짓점이 영향을 미치는 영역의 크기를 제어하는 것이다.
프래그먼트 처리기는 직접 뿌리기 연산을 수행할 수 없는데, 그 까닭은 격자상에서 각 프래그먼트의 위치는 프래그먼트가 생성될 때 고정되어 프로그래머가 변경할 수 없기 때문이다. 그러나 논리적 흩뿌리기 연산은 추가적인 수집 단계로 때때로 재투사 또는 구현될 수 있다. 흩뿌리기를 구현하려면 먼저 출력값과 출력 주소 모두를 송출해야 한다. 그 직후의 수집 연산은 주소 비교를 통해 출력 값이 현재의 출력칸에 맞아떨어지는지 확인한다.

수집 
프래그먼트 처리기는 텍스처를 임의 방식으로 읽을 수도 있어서 단수/복수의 어떤 격자 칸으로부터도 원하는 대로 정보를 모을 수 있다.

정렬 
정렬 연산은 순서 없이 뒤섞인 요소 집합을 순서에 따라 정렬된 요소의 집합으로 변환한다. 가장 일반적인 GPU 구현은 정렬망을 이용하는 것이다.

탐색 
탐색 연산으로 프로그래머는 흐름 안의 특정 요소 또는 특정 요소의 이웃 요소를 찾을 수 있다. GPU는 한 요소를 찾는 속도를 올리기 위해 사용되지는 않지만 대신 여러 탐색을 병렬로 실행하는 데 사용된다.

자료 구조 
다양한 자료 구조가 GPU상에서 표현될 수 있다:
 고밀도 배열
 저밀도 배열 - 정적, 또는 동적
 적응 구조

기타 
GPGPU로 CPU 한 개에 비해 100배~250배의 속도 향상을 이룰 수 있지만, 병렬도가 지극히 높은 응용 프로그램에서만 이 정도의 혜택을 볼 수 있을 것이다. 한 개의 GPU 처리 코어는 데스크톱 CPU의 코어 한 개와 대등하지 않다.

같이 보기 
 컴퓨터 클러스터
 컴퓨터 비전
 생물정보학
 디지털 화상 처리
 영상 분할
 CUDA
 라라비 (마이크로아키텍처)
 텐서 처리 장치

참고 문헌

외부 링크 
  gpgpu.org
  GPGPU 위키

 
그래픽 카드
마이크로프로세서
병렬 컴퓨팅
비디오 게임 개발
계산과학
신기술
명령어 처리