微軟發表用於 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)》,不過說實話細節也不多。另外,在 Channel 9 上可以找到展示的影片(網頁簡報投影片),裡面有簡單的矩陣相乘範例程式碼;而在《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(concurrency 現在是微軟 PPL 的 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 自己負責處理掉了(可以在轉換成 array_view 時,以 constwriteonly 來做一定程度的控制);而就像範例裡面一樣,再搭配 lambda expression 的話,更是連 kernel function 都不用額外定義了~如此一來,就真的很像在寫一般的 C++ STL algorithm 的迴圈程式,雖然比不上 OpenMP,但是也是相當地簡單了!

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

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

不過由於微軟是把 C++AMP 當作一個開放規格(open specification),所以理論上其他編譯器應該也可以自己支援 C++ AMP 的語法。而如果 restrict() 可以有 direct3dcpu 以外的選項的話,那其他編譯器應該也可以用 DirectCompute 以外的方法來實作(例如 OpenCL),這樣應該就可以跨平台支援了。不過這樣一來,重點就是有多少編譯器願意支援了。

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

對「微軟發表用於 GPU 大量平行計算的 C++ AMP」的想法

  1. 运行效率方面肯定要打折扣罗。 我这几天对比了一下自己的code 的opencl版 和cuda版在同一张GTX卡上的效率,就有2倍多的差距。最近心痒手贱想做assembler了。。。。

    • 基本上,用低階的東西,效能一定越好啊~(好啦,大部分情況啦)
      不過在 coding 的難易度上,也是會有差別的 ^^"

      而到目前為止,C++ AMP 應該是 Heresy 看到最接近傳統 C++ 程式開發的寫法了~

  2. 這樣說好了, 為啥 OpenMP 跟 M$ 這個都導入一些 keyword/Function/Class….(實際上也是keyword) 明白告訴 compiler 之後的資料可以平行處理, 就是為了讓compiler清楚資料是怎樣計算的, 不然用for loop 就好 幹麻要增加這些東西! 一切都是為了讓compiler更清楚知道 這段 code的意圖, 方便最佳化的處理

  3. 我覺的會有 Super class 集合的C+++ compiler 出現, 融入有 FORTARN 的元素, 方便以後開發這類須要大量concurrency 這樣才是王道 XD

      • fortran没什么优点了,科学计算还是fortran普遍无非是为了兼容legacy code,比如lapack blas这些都是前人用fortran写出来的。我能想到的优点也就是fortran在语言层面实现了matrix,不过也还是很基本。

      • 誒~我是路人,路過看到忍不住發表一下自己的感想XD
        我是做CFD的,但是我不是專攻CFD演算法設計跟高速計算程式開發的那一領域,我主要的主題還是在流場研究,所以寫CFD程式只是輔助手段,僅僅讓我有個工具去做流場研究而已。
        程式跟硬體理論方面的東西我是不懂得,但是我又得自己寫程式,所以對我這種非專業人員,用來寫科學計算的語言的要求就是
        1.語法簡單好用、自然 2.不用花心思在程式最佳化就可以有一定的速度
        我大學時代學校是學C,但後來要實際寫CFD程式時,數值分析小程式還OK,但是大型的解算器就寫得比較累,邏輯該怎麼寫都知道,但是實際寫起CODE就是一下這裡寫錯一下那裏語法寫錯。可是後來我到了一間用FORTRAN的實驗室後,我發覺FORTRAN的語法很好學,很貼近自然數學,尤其在矩陣操作上的寫法很貼近一般手寫數學,還有要配置動態記憶體陣列時,也簡單很多,不需要用到指標和雙重指標,尤其有些動態記憶體配置的陣列維度都四五維以上,用指標真的很煩。還有參數傳遞預設就是傳址,不需要像C還要用到參考或是指標,沒有大量經驗無法熟用。
        前一陣子實驗室也要做GPU運算,剛開始學CUDA C,也是覺得很頭大,但是後來還是回去改用CUDA FORTRAN,就覺得還是FORTRAN的語法好用。
        至於C++我沒學過,不過我想表達的意思就是:我想我們這種外行人看FORTRAN好不好用的角度應該會跟你們專業的不太一樣。就像買車時,也許你們是專業玩車的,你們會在意性能,而我只是普通人得買車上下班,在意的只是椅子舒不舒適,外觀好不好看而已。

        • 感謝你的意見~
          的確,Heresy 認識在寫程式的人大部分好像都是以資工背景出身的為主,的確是沒有從這個角度來看過 ^^"

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.