SSブログ

OS XのOpenCL - その15 [OS XのOpenCL]

ちんたらやってるOS XでのOpenCL。ここからやっとこのガイドの白眉と言えるチューニングの話になる。今日の部分は当たり障りのない一般的な話しから入るけど、そのうち非常に濃い話になってくるので、面白くなる前に投げないようにしないといけない.....

13  GPUでのパフォーマンスのチューニング

GPUとCPUでは根本的に異なるアーキテクチャを持っているので、OpenCLには異なる最適化が必要となる。CPUは相対的に少ない計算エレメントと多いメモリ(大きなキャッシュと基板上にさらに大きなRAM)を持っている。GPUは相対的にたくさんの計算エレメントと通常はCPUより少ないメモリを持っている。従って、GPUで最速のコードはメモリの使用量は少なく、たくさんある計算パワーを利用するように設計されるだろう。さらに、GPUのメモリはそのメモリアーキテクチャにマッチしたアクセスパターンでアクセスされるとき速いので、これを頭に入れて設計されるべきである。

CPUとGPUと両方で効率的に実行されるOpenCLのコードを書くことは可能である。しかし、最高のパフォーマンスを得ようとするなら、ふつうは2種類のデバイスに対して異なるコードを書くことになる。

このチャプタではGPUでのパフォーマンスを改善することに集中する。まずチューニングによって得られるGPUでの重大なパフォーマンスの改善について示す。そのあと、コードの実行時間を測定するためのAPIをリストし、GPUデバイスでの最高パフォーマンスを見積もる方法を示し、GPUパフォーマンスのチューニングのために従うべきプロトコルを示し、パフォーマンスを改善する例を見てみる。チャプタの最後の表14-1はたいていのGPUに使えるパフォーマンス測定と改善の一般的に適用できる提案になっている。

注意:このチャプタはWWDC2011の「What's new in OpenCL」講演に基づいている。

13.1  なぜチューニングが必要か

GPU向けのOpenCLコードのチューニングは2倍から10倍パフォーマンスが改善する場合がありえる。図-9は16Mピクセルの画像に対してガウシアンブラーを実行するアプリを最適化したときの典型的な計算スピードの改善の例を示している。
0722fig09.png
このコードの最適化のプロセスはあとの例に示してある。

13.2  コードを最適化する前

最適化の前には
  1. ほんとうにコードに最適化が必要かどうかを決定する。最適化にはそれなりの時間と努力が必要である。最適化の努力を開始する前に最適化に要するコストと恩恵を秤にかけよう
  2. 最大パフォーマンスを見積もっておく。単純なカーネルをGPUデバイスで走らせて能力を見積ろう。カーネルが実行にどのくらいの時間を費やしているか測定するために使えるテクニックを示す。メモリアクセスと計算スピードをテストするのに使えるコード例を示す
  3. 最適化の繰り返しごとのサンプリングデータを集める。サンプルコードのうち最適化していないものを走らせて結果を保存する。それから最適化のそれぞれの段階のコードを同じデータに対して走らせて結果を比較し、コードの変更によってかえって悪くなったしてないことを確認する

13.3  パフォーマンスの測定

OpenCLアプリの最適化のポイントは速く走るものを得ることである。最適化のそれぞれの段階で最適化したコードがどのくらい速くなったかを知ることが必要である。カーネルが実行にどのくらい時間を使っているかを知るためには
  • カーネルを呼び出す直前にタイマをスタートする。それには
    cl_timer gcl_start_timer(void);
    
    を使う。この関数はタイマを止めるためのcl_timerを返す
  • カーネルから返ったらすぐ
    double gcl_stop_timer(cl_timer timer);
    
    を呼ぶ。この関数はgcl_start_timer関数を呼んで作ったタイマを止める。これはgcl_start_timer関数を呼んでからの秒単位の経過時間を返す
連続して複数回カーネルを呼んで実行時間を測定すれば結果の信頼性は改善される。デバイスを「ウォームアップ」することがベンチマークの結果の一貫性を改善できるので、時間を測る前に少なくとも1回はカーネルを呼ぶようなコードにした方がいい。つぎのリスト14-1はキューに投入されたカーネルのパフォーマンスに関する情報を保存する。繰り返しのインデクスが-2から始まっていることに注意。計測は0に達してから開始される。
const int iter = 10; // ベンチマークの繰り返し回数
cl_timer blockTimer;
for (int it = -2; it < iter; it++) { // 負の値は計測しない:ウォームアップ
    if (it == 0) {                     // 計測開始
        blockTimer = gcl_start_timer(void);
    }
    <code to benchmark>
}
clFinish(queue);
gcl_stop_timer(blockTimer);

// t = 繰り返し1回分の実行時間
double t = blockTimer / (double)iter;

nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。