Search

[미리보는 C++ AMP-3] array와 array_view

DirectX 11 2011.12.21 08:00 Posted by 조진현

들어가기 앞서 지금까지 AMP가 GPU를 활용하는 프로그래밍 기법이라고,
제가 지속적으로 언급해 왔었습니다.
사실 이 말은 적절하지 않는 표현이였습니다.

얼마 전까지만 해도, 개발자에게 주어지는 프로세싱 유닛은 CPU와 GPU 뿐이였습니다.
CPU는 개발자의 활용 영역에 있었지만, GPU는 제한적으로 사용할 수 있었습니다.
왜냐하면 GPU를 사용하기 위해서는 DirectX API 사용이 필수였기 때문입니다.
그 DirectX 의 영역을 일반적인 개발자 영역으로 확장하는 것이 C++ AMP 입니다.
그런데 최근에 CPU와 GPU를 통합한 APU 라는 것이 등장했습니다.
앞으로 또 다른 프로세싱 유닛이 등장할지도 모르는 일입니다.
그래서 이런 프로세싱 유닛들을 통합한 용어가 필요하게 되었고,
C++ AMP에서는 이를 accelerator 라고 합니다.
즉, CPU와 GPU 그리고 APU 가 이 accelerator 에 속한다고 할 수 있습니다.
accelerator 는 C++ AMP 코드가 실행될 수 있는 이런 타겟을 표현합니다.
그래서 C++ AMP는 이 accelerator를 활용하는 프로그래밍 기법이라고
해석하는 것이
더 적절한 표현입니다.
앞으로 이 accelerator 라는 표현을 많이 사용할 것이니 확실히 알아두시기 바랍니다.


앞서 간단하게 작성했던 샘플을 다시 한번 보겠습니다.
 

void AddArrays(int n, int * pA, int * pB, int * pC)

{

    array_view<int,1> a(n, pA);

    array_view<int,1> b(n, pB);

    array_view<int,1> sum(n, pC);

 

    parallel_for_each(

        sum.grid,

        [=](index<1> i) restrict(direct3d)

        {

            sum[i] = a[i] + b[i];

        }

     );

}



array_view 라는 것이 먼저 눈에 보입니다.
C++ AMP 에서는 대규모 메모리를 의미하는 클래스로
array 와 array_view 라는 것이 있습니다.
기본적으로 이 두 클래스의 목적은
accelerator 상으로 데이터를 옮기기 위함 입니다.


array 의 경우에는 실제 데이터 배열입니다.
STL 의 컨테이너와 유사합니다.
반면 array_view 는 데이터 배열의 일종의 래퍼( wrapper ) 입니다.
그래서 array_view 는 STL의 이터레이터( iterator ) 와 유사한 동작을 합니다.
array_view는 한 번에 여러 데이터의 동시에 접근할 수 있으며,
랜덤 액세스( random-access ) 가 가능합니다.

array 에 의해서 정의되는 배열 데이터는 accelerator 상에 메모리를 가지게 됩니다.
이것은 개발자가 직접 정의해서 할당할 수도 있고,
런타임( runtime ) 에 의해서 자동적으로 생성될 수도 있습니다.
그렇기 때문에 실제 데이터가 생성되어질 때 깊은 복사( deep-copy )를 하게 됩니다.
우리가 일반적으로 오브젝트를 메모리에 생성했을 때와 같다고 생각하시면 됩니다.
array 는 다음과 같이 사용할 수 있습니다.( 샘플은 msdn 에서 가져왔습니다 )

vector<int> data(5);
for (int count = 0; count < 5; count++)
{
    data[count] = count;
}

array<int, 1> a(5, data);

parallel_for_each(
    a.grid,
    [=, &a](index<1> idx) restrict(direct3d)
    {
        a[idx] = a[idx] * 10;
    }
);

data = a;
for (int i = 0; i < 5; i++)
{
    cout << data[i] << "\n";
}



반면에 array_view는 이름에서 유추할 수 있듯이,
실제 데이터들은 다른 accelerator 상에 있고,
이를 연산을 위해서 복사를 하는 개념
입니다.

즉, 커널 함수가 실행될 때, 데이터가 복사됩니다.
( 커널 함수는 AMP 내의 람다 함수 부분을 의미합니다. )

이 array_view 개념은 DirectX11 에서 보셨던 분들은 쉽게 이해할 수 있는 개념입니다.
바로 ComputeShader 를 위해서 데이터들을 연결하는 바로 그 개념이기 때문입니다.
아래의 그림은 ComputeShader 의 동작 방식을 보여주는데,
SRV( shader resource view )와 UAV( unordered access view ) 라는 것이
결국 view 의 역할을 하는 것입니다.




DirectX11 과 연계해서 생각한다면,
array 라는 메모리 배열도 결국 텍스쳐 메모리라는 것을
눈치챌 수 있을 것입니다.
DirectX10 부터 텍스쳐 인터페이스는 꼭 이미지 데이터를 의미하지 않습니다.
대용량의 메모리 블럭의 의미에 더 가깝다는 것을 알아두시기 바랍니다.
텍스쳐의 개념을 사용하기 때문에 동시에 여러 데이터에 접근이 가능하고,
랜덤 액세스도 가능한 것입니다.^^

신고

[미리보는 C++ AMP-2] C++ AMP 맛 보기

DirectX 11 2011.12.01 08:00 Posted by 조진현


백문이 불여일견이라고들 하죠?
글로써 언급하는 것보다,
프로그래머들은 코드로 볼 때 더 직관적인 이해를 할 수 있는 경우가 많습니다.

간단하게 두 배열의 합을 구하는 코드를 통해서,
이를 AMP 적으로 어떻게 작성하는지를 보겠습니다.

아래는 우리가 일반적으로 생각할 수 있는 CPU를 활용해서
합을 구하는 코드입니다.

void AddArrays(int n, int * pA, int * pB, int * pC)

{

   for (int i=0; i<n; i++)

   {

      pC[i] = pA[i] + pB[i];

   }

}


자세한 설명은 생략해도 될 것이라 생각합니다.^^
아래는 C++ AMP로 작성된 합을 구하는 코드입니다.

#include <amp.h>

using namespace concurrency;

void AddArrays(int n, int * pA, int * pB, int * pC)

{

   array_view<int,1> a(n, pA);

   array_view<int,1> b(n, pB);

   array_view<int,1> sum(n, pC);

   parallel_for_each( sum.grid,

                                          [=](index<1> i) restrict(direct3d)

   {

      sum[i] = a[i] + b[i];

   }   );

}


 

위의 AMP 구현 부분에서 색상이 들어간 부분이 CPU를 활용한 부분과 다른 부분입니다.
코드량이 증가해버린 단순한 사실을 우리는 확인할 수 있습니다.
코드가 증가한 가장 기본적인 이유는 메모리 문제입니다.
우리가 지금까지 C++ 에서 사용하는 메모리는 CPU 가 접근할 수 있는 시스템 메모리입니다.
이 메모리를 GPU 로 처리하기 위해서는 GPU가 직접적으로 접근 가능해야 합니다.
그런데 C++ 에서 할당한 메모리는 GPU가 접근할 수가 없습니다.
그래서 비디오-메모리에 시스템-메모리의 데이터를 복사하는 과정이 필요합니다.
그 과정이 바로 코드의 증가를 불러오는 것입니다.
( 복사라고 보기는 조금 모호합니다만, 지금은 그냥 넘어가겠습니다. )

이 증가한 코드들에 대해서 지금부터 살펴보겠습니다.


#include <
amp.h>

using namespace concurrency;


AMP를 사용하기 위한 헤더의 선언입니다.
기본적으로 AMP를 사용하기 위해서는 람다식과 concurrency  에 대한 이해가 있어야 합니다.


array_view
<int,1> a(n, pA);

array_view<int,1> b(n, pB);

array_view<int,1> sum(n, pC);

이 부분은 앞서 언급했던 GPU가 접근할 수 있는 메모리 영역으로
데이터를 만드는 부분입니다.
이 데이터를 만들 수 있는 메모리 영역이
array 와 array_view 라는 것으로
구분됩니다.
이 둘의 차이는 이후에 다루어 드릴테니,
지금은 GPU가 접근할 수 있는 메모리 영역으로 생각해 주셨으면 합니다.^^


parallel_for_each(
 ... ) restrict( direct3d )

c++ 에 main(...) 이 있다면, AMP 에는 parallel_for_each( ... ) restrict( direct3d ) 가 있습니다.
이 부분은 GPU가 연산을 시작하는 진입점( EntryPoint ) 입니다.

parallel_for_each를 잘 모르시는 분들은 아래의 링크를 참고하시 바랍니다.
http://vsts2010.net/123
더 자세한 사항은 이 블로그의 VC++ 10 Concurrency Runtime 카테고리를 참고하시기 바랍니다.

 

제가 단순하게 정리해 드리면,
기존에 VC++ 10 에서 사용되는 parallel_for_each 는 CPU를 활용해서 병렬적으로 처리하는 것이지만,
뒤에 restrict( direct3d )를 명시함으로써 이를 GPU에서 병렬적으로 처리
하도록 합니다.
 

이 진입 함수는 parallel_for_each(  람다식 ) 형태를 가지게 됩니다.
이는 GPU의 많은 스레드들에게 '이 람다식을 각각 실행해 주세요' 라고 명령을 내리는 것입니다.
역시 람다( Lambda ) 에 대해서 잘 모르시는 분은 옆의 카테고리에서
c++0x 를 보시기 바랍니다.
람다의 첫번째 설명 링크는 아래와 같습니다.
http://vsts2010.net/73

 

그러면 얼마나 많은 스레드들이 람다식을 실행해야 하는지에 대한 명시가 있어야 합니다.
그것이 바로 paralle_for_each( ... ) 의 첫번째 인자인 sum.grid 입니다.

grid 에 대한 설명은 뒷부분에서 자세히 다루겠으니,
지금은 스레드 갯수에 대한 정의로 보시면 충분합니다.

람다식의 인자로 index<1> idx 가 보이실 것입니다.
이 인자는 람다식에 전달되는 스레드들의 ID들입니다.
이 ID들을 통해서 스레들을 식별할 수 있습니다.
스레드들의 ID를 통해서 배열 형태의 데이터를 캡쳐해서 값을 저장하는 것입니다.

간단한 프로그램이지만, 사실 이런 형태가 C++ AMP의 전부입니다.^^

물론 이렇게 간단히 끝나면 무척 행복하겠지만,
난이도는 역시 알면 알수록 높아집니다.^^


본 글에서 사용된 예제들은 MS에서 사용된 예제들입니다.
제가 구현한 것들이 아님을 알려드립니다.^^

신고

GPU를 활용하는 일은 모든 개발자에게 열려있는 길이여야 합니다.
하지만 DirectX를 직접적으로 활용해야만 하는
MS의 GPGPU 플랫폼인 DirectCompute는 그렇지가 않습니다.

그래픽카드라는게 원래 특수한 목적성을 가지고 등장한 장치이기 때문에,
이를 활용하는 사람들 또한 특정 영역에 국한되어 있는게 현실입니다.
'이제부터 GPGPU 를 적극 활용합시다!' 라고 생각을 하더라도, 
실제로 그것을 활용하기 위한 진입 장벽은 굉장히 높을 수 밖에 없습니다.

그러면 어떻게 해야만 이 장벽을 조금이라도 낮출 수 있을까요?
엔비디아의 CUDA 를 보면, 힌트가 있습니다.
하지만 몰라도 상관없습니다.^^
C++ 파일 내에서 컴파일러에 의해서 자동적으로 처리가 될 수 있으면 가장 좋지 않을까요?
순수 C++ 의 기능만 사용해서 컴파일러가 자동적으로 처리해 준다면,
개발자는 DirectX와 ComputeShader 에서 해방될 수 있을 것입니다.
그것이 바로 C++ AMP 가 등장하는 배경
입니다.
C++ AMP는 다음 버전의 VisualStudio 에 탑재 되어져서 등장할 예정이라고 합니다.


어떤 함수가 아래와 같이 있습니다.
void Func( ... )
{
    코드
}

위의 함수는 결국 컴파일러에 의해서 CPU 와 관련한 명령어를 생성하게 됩니다.
이를 AMP 적으로 확장하면 정확히 아래와 같이 구성됩니다.
void Func( ... ) restrict( cpu )
{
   코드
}

restrict 이라는 키워드를 함수에 적용함으로써 간단히 이를 구현합니다.
눈치가 좀 빠르신 분들이라면
'저 cpu를 gpu 로만 변경하면, gpu 로 컴파일 되어지는 것인가?' 라고 생각이 드실 겁니다.
네. 맞습니다.
그것이 바로 C++ AMP 가 DirectCompute 를 구현하는 방법입니다.
정확히는 아래와 같습니다.
void Func( ... ) restrict( direct3d )
{
   코드
}
'direct3d' 가 바로 'gpu' 를 의미합니다.
현재 이 옵션용 예약어는 확정적인 것은 아닙니다.
'direct3d' 가 확정될 수도 있고, 그렇지 않을 수도 있습니다.
아직 C++ AMP가 출시되지 않아서 유동적인 부분이 있습니다.
그 점 주의해서 읽어주시기 바랍니다.^^

다음 버전의 Visual C++ 부터는 
함수마다 저렇게 restrict 한정자에 컴파일 옵션을 지정해주어야 합니다.

물론 지정을 하지 않았을 때는, 디폴트로 restrict( cpu ) 로 자동 처리할 것입니다.

그러면 한 함수 내에서 CPU와 GPU를 활용해야 하는 경우는 어떻게 해야할까요?
void Func( ... ) restrict( direct3d, cpu )
{
   GPU를 사용하는 코드
   CPU를 사용하는 코드
}

위와 같이 혼합해서 사용하는 것도 가능합니다.
또한 오버로드와 관련한 이슈도 문제 없이 처리될 것입니다.
void Func( ... );
void Func( ... ) restrict( direct3d );

간단히 위와 같이 restrict 만으로 GPU를 사용하는 것이 완전히 된다면 얼마나 좋겠습니까만,
restrict( direct3d ) 로 정의되어지는 함수들은 그에 상응하는 규칙으로 코딩 작업을
해야만 합니다.
이것이 사실 그렇게 쉬운 개념만으로 이해할 수 있는 것은 아닙니다.
하지만 DirectCompute를 직접 제어하는 것보다는 쉽습니다.

다음 시간부터 C++ AMP 로 프로그래밍 하는 개념에 대해서 살펴보겠습니다.^^
신고

C++ AMP

Visual C++ 10 2011.06.28 09:00 Posted by 흥배

C++ AMP라는 것을 들어보셨나요?근래에 나온 단어입니다.

AMP AcceleratedMassive Parallelism의 약자로 병렬 프로그래밍과 관련된 것입니다.

 

C++ AMP 2주 전의 AMD Fusion 컨퍼런스에서MicrosoftHerb Sutter씨가(MS의프로그램 언어 아키텍터 이자 C++ 표준 위원 멤버) 처음으로공개한 것으로 다음 버전의 Visual Studio(현재는Visual C++)에서 GPGPU 프로그래밍환경을 제공하는 것을 뜻합니다.

 

병렬 프로그래밍에서 대해서 조금 깊게 공부하신 분들은 아마 GPGPU라는것을 들어본 적이 있으리라 생각합니다. GPGPU는 간단하게 말하자면 GPU CPU 처럼 사용하자라는 것으로 GPU의 높은 성능을 사용하여 CPU와 똑 같게는 사용할 수는 없지만연산 처리에서 높은 병렬 기능을 사용하여 CPU보다 훨씬 뛰어난 결과를 얻을 수 있습니다.

 

현재까지 GPGPU 개발환경은NVIDIA Cuda와 오픈 아키텍처인 OpenCL,DirectX 베이스의 DirectCompute가 있습니다.

 

GPGPU 프로그래밍의 단점은 프로그래밍이 복잡하고 아직 레퍼런스가적다는 단점이 있어서 아직은 일반적인 프로그래밍 영역에 들어오지 못하고 있습니다(사실 아직 일반 병렬프로그래밍도 쉽게 사용하지 못하고 있으니..). 그래서 GPGPU가나온 것은 몇 년이 지났지만 아직 일부 전문 영역에서만 사용되고 있었습니다.

 

그러나 CPU 아키텍처가 멀티코어에서 헤테로지니어스 아키텍처(이기종의 CPU가 결합.CPU+GPU)로 서서히 넘어가고 있어서 자연스럽게GPGPU 프로그래밍이 부각되고 있었습니다. 하지만 아직도 개발환경의 뒷받침이 부족한 상태였는데드디어 우리 개발자에게 친숙한 Visual C++에서 이런 문제를 해결하려고 합니다.

 

C++ AMP는 쉽게 말하면Visual C++에서 GPGPU 프로그래밍을 지원하는 것을 말합니다. Visual C++의 뛰어난 개발환경을 토대로 하여 이때까지 복잡했던GPGPU 프로그래밍을 일반 프로그래밍 하듯이 사용할 수 있게 해줍니다. 이로써 GPGPU가 일반 프로그래밍 영역으로 들어 올 수 있는 큰 계기가 되었다고 생각합니다.

 

 

C++ AMP에 대해서 AMD Fusion 컨퍼런스에서 데모를 시연한 Daniel Moth의 블로그에올라온 글을 정리하면

개발자의 생산성과 이식성을 저해하지 않고 헤테로지니어스 하드웨어 프로그래밍의 허들을 낮게 하여 프로그래밍 일반영역에서 사용할 수 있도록 한다.

 

현재의 대 규모 병렬 하드웨어(CPU GPU)의 사용을 돕기 위한 것만이 아닌 코드의 투자를 미래에 대비한 디자인으로 하여 견고하도록 한다.

 

Visual Studio의 일부분으로 또 다른 컴파일러나 다른 구문을배울 필요가 없다.

 

현재의 C++ 언어를 사용하며 C나다른 파생 언어가 아니다.

 

Visual Studio vNext와 완벽하게 통합하여 지원한다. 편집, 빌드, 디버그, 프로파일러 등 Visual Studio의 다른 모든 기능이 C++ AMP와 같이 동작한다.

 

기존의 Concurrency Runtime의 일부로 STL와 비슷한 형태의 라이브러리를 제공하여 amp.h 헤더 파일을제공한다.

 

병렬화를 주 특징으로 하여 헤테로지니어스 하드웨어 위에서 거대한 다 차원 데이터를 아주 쉽게 동작한다.

 

유일의 코어 C++ 언어 확장을 도입한다.

 

DirectX(DirectCompute) 위에 구축하지만 C++ AMP에서는 DirectX의 모습은 나타나지 않는다( DirectX를 몰라도 상관 없다).

 

 

 

또 동 세미나에서 기조 연설을 한 Herb Sutter씨의 강연 중 C++ AMP에 관한 내용으로는

C++ AMP에 의해서 기존의C++에서 큰 변경을 가하지 않으면서 언어를 확장하는 점을 강조하여 새로운 언어가 만들어서 개발자에게 혼란을 주는 것을 피했다라는것을 알림.

 

언어 확장으로 restrict() 함수와 array_view라는 2개의 type Key로 잡음. restrict()는 프로세서 아키텍처에 따라서 실행가능한 기능에 제한을 거는 것이고 array_view는 불 균인한 메모리 공간으로의 접근으로 생기는문제를 회피하기 위한 것으로 메모리 공간을 N 차원의 배열로서 작업하는 것을 뜻한다. 메모리 공간의 추상화라고 할 수도 있다. restrict()array_view는 프로세서 아키텍쳐와 메모리 공간의 차이를 흡수할 수 있는 것으로 C++ AMP의 중요한 Key이다.

 

C++ AMP의 컴파일러는Visual Studio의 차기 버전에서 들어갈 예정으로 릴리스는 이번 연말로 예상하고 있다. 또이 컴파일러는 오픈 사양일 예정으로 Windows 상의 VisualStudio 뿐만이 아닌 그 이외의 개발 환경(C++ Builder이나 이클립스 등)에서도 이용할 수 있도록 AMD와 협력 하여 개발 중이라고 한다.

 

 

 

 

 

DirectXDirectCompute를 사용한다고 하니 C++ AMP를 사용한 프로그램은 Windows Vista 이상에서만 사용할 수 있을 것 같습니다(이유는 DirectCompute DirectX 10에서 지원하기 때문).

 

GPGPU에 관심은 있었지만 아직 시기상조라고 생각하는 분들은 C++ AMP가 나오면 개발 허들이 크게 내려가므로 본격적으로 준비를 해도 좋을 것 같습니다. AMD에서는 헤테로지니어스 컴퓨팅 프로그래밍의 전망을 CUDA 등의독자 사양에서 OpenCL이나 DirectCompute 등의오픈 사양으로 이동하고, 전문 프로그래머만 프로그래밍 하는 시대를2011년까지로 보고 그 이후로는 일반 프로그래머가 완전하게 C++로 프로그래밍하는 헤테로지니어스컴퓨팅 프로그램이 올 것으로 보고 있다고 합니다.

 

저도 이제 슬슬 GPGPU 프로그래밍 쪽으로 들어가볼 예정인데 일단조만간 OpenCL부터 시작해 볼까 합니다. 연말에 VS vNext가 나올 수도 있다고 하니 그때 꼭 C++ AMP를 공부해서 그 내용을 공유하도록 하겠습니다^^

 

 

 

참고

헤테로지니어스 멀티 코어 http://jacking.tistory.com/513

 

Daniel Moth씨의 블로그 http://www.danielmoth.com/Blog/

  위 글을 정리한 한블로그(일본어)
 http://blogs.msdn.com/b/hiroyuk/archive/2011/06/20/10176783.aspx

 

AMD Fusion 컨퍼런스에서의 데모

비디오 http://channel9.msdn.com/posts/Daniel-Moth-Blazing-fast-code-using-GPUs-and-more-with-C-AMP

슬라이드 http://ecn.channel9.msdn.com/content/DanielMoth_CppAMP_Intro.pdf

 

일본의 임프레스 사이트에 올라온 Herb Sutter씨의 기존 강연정리 글

http://pc.watch.impress.co.jp/docs/news/event/20110617_453939.html

 

저작자 표시
신고