微軟發表用於 GPU 大量平行計算的 C++ AMP

自從 nVIDIA CUDA 推出後,把顯示卡的繪圖晶片拿來做通用計算的 GPGPU 算是用來越普及了∼目前除了 CUDA 外,AMD 也有自己的 Stream SDK(目前以改名為 AMD APP),同時也更有跨硬體的標準 OpenCL 以及微軟 DirectX 裡的 DirectCompute(MSDN)。

而微軟在這幾天 AMD Fusion Developer Summit  上,也發表了一個用於 C 的 GPU 大量平行化的開發技術,命名為「C Accelerated Massive Parallelism」。目前似乎還沒有能找到詳細資料,不過就現在看到的資料,重點是在於:

  • STL-Like、C 的語法,只要會用 STL 就會用。也就是他的學習門檻會更低∼
  • 會整合在下一版的 Visual C 中,不需要額外的編譯器也不需要其他的語法。
  • 基於微軟的 DirectCompute(這代表了他應該只能用在 Windows Vista / Windows 7)。
  • 將可以使用在 GPU,以及 APU 等適合大量平行計算(massively parallel)的硬體上。

詳細的資料可以參考《C Accelerated Massive Parallelism》和《Introducing C Accelerated Massive Parallelism (C AMP)》,不過說實話細節也不多。另外,在網路上可以找到展示的影片(網頁),裡面有簡單的矩陣相乘範例程式碼;而在《Microsoft brings GPU computing to C with C AMP》一文中,也有一些簡單的說明。

其中,在《Microsoft brings GPU computing to C with C AMP》一文中可以看到,C AMP 主要是在 C 裡加上了一些必要的新的 class,包括了:array<T, N>array_view<T, N>index<N>grid<N>tiled_grid<Z, Y, X> 等等。

另外,也針對平行化處理的需求,加上了 parallel_for_each() 的函式,以及額外的 restrict(direct3d, cpu) 指令。

在投影片中所提供的「Hello World」範例,則是簡單的矩陣相加∼如果直接用 C 寫的話,會是:

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> idx ) restrict(direct3d)
{
sum[idx] = a[idx] b[idx];
}

);
}

而在影片(網頁)裡面的範例程式碼,則是提供了另一個、稍微複雜一點的矩陣相乘的程式碼當作例子。用 C 寫的話,會是:

void MatrixMult( float* C, const vector<float>& A, const vector<float>& B,
int M, int N, int W)
{
for( int y = 0; y < M; y )
for( int x = 0; x < N; x )
{
float sum = 0;
for( int i = 0; i < W; i )
sum = A[y*W i] * B[i*N x];
C[y*N x] = sum;
}
}

改使用 C AMP 則會變成(還要加上 #include <amp.h>、且部分的型別的 namespace 是 concurrency):

void MatrixMult( float* C, const vector<float>& A, const vector<float>& B,
                 int M, int N, int W) 
{
array_view<const float, 2> a(M,W,A), b(W,N,B);
array_view<writeonly<float>, 2> c(M,N,C);

parallel_for_each( c.grid, [=](index<2> idx) restrict(direct3d){
float sum = 0;
for( int i = 0; i < a.x; i )
sum = a(idx.y,i)*b(I,idx.x);
c[idx] = sum;  }
);
}

以上面兩個範例看來,C AMP 的平行計算方法,目前似乎只有一個 parallel_for_each()?而在使用上,的確真的很接近 STL 的用法∼要使用時,應該就是:

  1. 加上 <amp.h> 這個 header 檔、並指定 namespace。
  2. 將資料轉換為 C AMP 的特定型別(這邊都是用 array_view
  3. 將實際要執行的程式以 function object 的形式,和資料一起丟給 parallel_for_each(),做平行化的處理。

在這兩個範例裡,都是使用 C 0x 的 lambda expression(參考)的形式來建構所需要的 function object;而其中比較特別的是,對於這個 function object 他還加上了 restrict(direct3d) 這樣的程式碼。雖然以目前的資料還說還不是很確定,不過 restrict() 這個指令應該是用來指定這個 function object 丟到 parallel_for_each() 後,是要在 CPU 還是 GPU 上執行了∼


在 Heresy 來看,這樣的語法如果和 nVIDIA CUDA 或是 OpenCL 來比的話,確實是又再進一步簡化不少了!基本上要做的,就是轉換成 C AMP 特定的資料型態、然後呼叫 parallel_for_each() 就好了∼資料到底在 CPU 還是在 GPU 上,應該也是由 C AMP 自己負責處理掉了;而就像範例裡面一樣,再搭配 lambda expression 的話,更是連 kernel function 都不用額外定義了∼如此一來,就真的很像在寫一般的 C STL algorithm 的迴圈程式,雖然比不上 OpenMP,但是也是相當地簡單了!

不過如此一來,也代表大部分的 GPU 配置動作都是交給編譯器自動決定、處理了,所以接下來要擔心的,可能就是編譯器在自動最佳化方面的效率(包括了記憶體在 CPU / GPU 間的複製、以及平行化的工作配置)了∼

另外,由於這項功能是基於微軟 DirectX 11 中的 DirectCompute 來實作的,所以這也代表了 C AMP 這項技術也會被綁在 Windows Vista / Windows 7 以後的作業系統上,而不像 CUDA、OpenCL 可以跨平台了…這點對於要開發跨平台程式的人來說,就覺得比較悲哀了。

而由於這項功能是會整合在下一版的 Visual Studio 裡,所以看來短時間內應該都還沒有辦法玩到了。現在,也就只能慢慢期待,微軟趕快推出有這項功能的產品囉∼

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。