Xeon Phiのプログラミングについての情報(2) Offloadによるプログラミング

Xeon Phiを使ったプログラムの実行としては以下の方法がある。

  • Native実行用にコンパイルされた実行ファイルをXeon PhiにSSHでログインして直接プログラムを実行
  • MPIを使って実行
  • Offloadを使用したプログラムによる実行

今回はOffloadを使用したプログラムを作ったのでその解説を簡単にする。

Offloadを使用したプログラミングについて

Offloadを使用したプログラミングを簡単に説明するとGPUプログラミングのような使い方になる。

Intelが公開している資料等は以下のところにある。

実際のプログラミングはOpenMPの様にディレクティブを使ってXeon Phiへのデータ転送やプログラミングの指示を行う。

基本的な実行の流れは以下の通り。

  1. CPU側でXeon Phiで使用するデータの準備
  2. CPUからXeon Phiにデータの転送
  3. Xeon Phiで計算
  4. Xeon PhiからCPUに結果を転送
  5. CPUでXeon Phiの結果を用いて計算

今回Xeon Phiを使ったプログラムの練習とテストのために行列積のプログラムを書いた。コードは以下のところ。

Offloadによる記述の説明

Xeon Phiで実行する部分の指示

練習で書いたサンプルプログラムの中の./src/matrix_multiplication.cppのMultiplyMatricesMicImple()という関数の中で使っている。

まず以下のようなディレクティブによってXeon Phiで実行する部分を記述

#pragma offload target(mic) 
{
// Xeon Phiで実行される部分
}

Xeon Phiが複数あり、実行するXeon Phiを指定する必要がある場合は以下の様にする

#pragma offload target(mic:0) 
{
// Xeon Phiで実行される部分
}

上の例ではmic0というXeon Phiが使われる。この0で指定した部分には変数も使える。サンプルでは試しに変数で指定している。

Xeon Phi、CPU間のデータ転送

データを転送する場合は以下のようにして行う。

#pragma offload target(mic) \
  in(matrix_a:length(column_a*row_a) alloc_if(1) free_if(1)) \
  in(matrix_b:length(column_b*row_b) alloc_if(1) free_if(1)) \
  out(matrix_c:length(column_a*row_b) alloc_if(1) free_if(1))
{
// Xeon Phiで実行される部分
}

matrix_aはCPU側のポインタで、転送する要素数をlength(n)で指定、今は行列なのでcolumn_a*row_a個ある。その次のalloc_if(1) free_if(1)の部分ではディレクティブの開始時にメモリを確保、終了時に開放するように指定。指定しない場合と同じだが、今回は明示的にやっている。
メモリを使いまわすために確保、開放しない場合は1の部分を0にする。

そしてinはディレクティブの開始時にCPUからXeon Phiにメモリのコピーを、outは終了時にXeon PhiからCPUのメモリに転送を行うときに指定。

上の例ではmatrix_aとmatrix_bのデータを最初にCPUからXeon Phiに転送し、計算が終わり次第、matrix_cの内容をXeon PhiからCPUに転送していることになる。

他にもいろいろできるのでこれについては以下のチュートリアル資料等を参考に。
http://software.intel.com/sites/default/files/Beginning%20Intel%20Xeon%20Phi%20Coprocessor%20Workshop%20Offload%20Compiling%20Part%201.pdf

(ALLOC、FREEなどがdefineされてるらしいが、使うとコンパイル時にエラーになってできなかった・・・。だれか教えて)

Xeon Phiで処理するコード

ディレクティブ以下のブロック内については普通にOpenMPなどが使える。その部分についての説明は省略。
(今回のサンプルは何も考えずに普通にOpenMPで並列化したのでもっと頑張れよ!といわれるかもしれないがそのうち頑張ります。)

Offloadで書いたプログラムを実行する際の環境変数周り

OpenMPでスレッド数を指定する際に環境変数のOMP_NUM_THREASにスレッド数をセットするのが一般的だが、何もしないとCPUとXeon Phiで同じ環境変数を見にいってしまう。このため、Xeon Phiだけ指定したい場合はMIC_ENV_PREFIXを使う。

export MIC_ENV_PREFIX=M_

としておくと、M_から始まる環境変数Xeon Phiで使われる。
なのでXeon PhiでOpenMPのスレッド数を指定する場合は以下のようにする。

export MIC_ENV_PREFIX=M_
export M_OMP_NUM_THREAS=128

また、CPUやXeon Phiのデータのやり取りやXeon Phiの実行の様子を出力するには以下のように環境変数をセットする。

export OFFLOAD_REPORT=1 #2や3でより詳細なものが出せる

実験

2つの正方行列のサイズを1024*1024,2048*2048,4096*4096にしてその積を計算するときの計算時間を測定。
(CPUもBLASなどのライブラリを使っていませんのでその分も考慮してください)

実験環境
行列サイズ CPU 1コア (sec.) Xeon Phi 236スレッド (sec.) 高速化率
1024*1024 2.1 1.4 1.5
2048*2048 17.8 1.7 10.5
4096*4096 728.4 3.5 208.1

最大約200倍、CPUのソケットで比較すると理想的には約26倍くらいは出る計算。なので割りと速い気も・・・GPUと比べたらどうかは試してみないとよくわからないが。

頑張ってチューニングしたらどうなるかはきっとHPC屋さんがわんさか?やってるのでそちらを参照。

まとめ

どういうふうに書けばいいかわかれば割りとすぐに使える感じ。そこそこ速くなりそうなので今回は満足。
今後研究に使うかは謎。

他にもいろいろな制御ができるので公式のサンプルコードやチュートリアルを参考に。
情報のリンクのまとめは以下のところにある。
http://d.hatena.ne.jp/ang65/20130402/1364873045