2008年 3月 25日 火曜日 Sun Studio 12, コンパイラによるプログラムの自動並列化
Sun Studio 12 には自動並列化機能があり、コンパイラがループを解析して並列化を行います。並列化しても安全かどうかを、入れ子になったループも含む全ループにわたって解析し、安全と判断された場合には OpenMP を使って並列化を行います。ループ以外は解析も変更もされません。-xautopar コンパイルオプションを付けてコンパイルすることにより自動並列化が行われます。C/C++/Fortran に対応しています:
$ cc -xautopar foo.c
すでに OpenMP を使った並列化が実装されていたり、マルチスレッド化が行われたプログラムに対して使用することはできません。並列化が行われていないプログラムを変更することなくコンパイラによって自動的に並列化するのが、このオプションの目的です。
処理全体を把握するわけではなく、あくまでもループの解析のみに基づいて局所的に行われる並列化ですので、限定的な効果にとどまりますが、コードにまったく変更を加えることなくマルチコア/マルチソケット環境の恩恵を得ることができる可能性があるため、手元にあるプログラムの再コンパイルと実行が比較的容易な場合には、試してみる価値のあるオプションだと思います。
実際にどのループが並列化されたのか、という情報は -xloopinfo というオプションを追加することで標準エラー出力上で確認できます。
実行する際には OMP_NUM_THREADS 環境変数によって並列処理領域で使用するスレッド数を指定しておく必要があります。指定しない場合には OMP_NUM_THREADS=1 となります。
例によって「姫野ベンチ」で試してみました。処理時間のもっとも長い部分が並列化されなかったので、目に見える効果は現れませんでした。参考までにコンパイル時に -xloopinfo によって出力された内容を貼り付けておきます:
$ make cc -c -fast -g -m64 -DMIDDLE -xvector=simd -xdepend -xautopar -xloopinfo himenoBMTxps.c "himenoBMTxps.c", 152 行目: 並列化されます "himenoBMTxps.c", 153 行目: 並列化されません、利得なし "himenoBMTxps.c", 154 行目: 並列化されません、利得なし "himenoBMTxps.c", 170 行目: 並列化されます、逐次版が生成されました "himenoBMTxps.c", 171 行目: 並列化されません、利得なし "himenoBMTxps.c", 172 行目: 並列化されません、利得なし "himenoBMTxps.c", 195 行目: 並列化されません、安全でない依存性 (wrk2 p) "himenoBMTxps.c", 198 行目: 並列化されません、安全でない依存性 (gosa) "himenoBMTxps.c", 199 行目: 並列化されません、安全でない依存性 (gosa) "himenoBMTxps.c", 200 行目: 並列化されません、安全でない依存性 (gosa) "himenoBMTxps.c", 223 行目: 並列化されます、逐次版が生成されました "himenoBMTxps.c", 224 行目: 並列化されません、利得なし "himenoBMTxps.c", 225 行目: 並列化されません、利得なし cc -o bmt himenoBMTxps.o -fast -g -m64 -DMIDDLE -xvector=simd -xdepend -xautopar -xloopinfo
「安全でない依存性」と記されている部分は、変数がループ内に完全に閉じていないため (例えば一つ前のループ実行時の結果を参照している等)、並列化されなかったことを示しています。変数 gosa のために並列化されなかった198 行目から 200 行目にかけてのループが最も処理時間を要する部分なので、ここが並列化されなかった結果、全体のパフォーマンス向上には結びつきませんでした。
「並列化されます、逐次版が生成されました」という部分は、並列処理を行うものと逐次処理を行うものの両方が生成されたことを示しています。ループ内での処理が少ない場合には逐次版が実行されると説明されていますが、詳細はちょっとわかりませんでしたので引き続き調べたいと思います。上記のコンパイル結果を実行した限りでは、並列化された部分は常に並列処理が実行されていました。
試しに gosa を計算しないようにして、-xautopar の有無によるパフォーマンスの変化を見てみました。演算を一つ省いてしまっていますから姫野ベンチのこれまでの実行結果と比較できる値ではありませんが、並列化の効果を知る上での参考値にはなると思います:
-xautopar を付けてコンパイルした実行ファイルは環境変数 OMP_NUM_THREADS=2 を設定した上で実行しています:
| CPU | 2 次キャッシュ | メモリ |
|---|---|---|
| AMD Opteron 250 (2.4GHz) x2 | 1 MB | 3326 MB |
結果 (5 回計測した中央値)
| コンパイルオプション | 結果 (MFLOPS) |
|---|---|
| -DMIDDLE -fast -m64 -xvector=simd -xdepend -xrestrict | 801.706509 |
| -DMIDDLE -fast -m64 -xvector=simd -xdepend -xrestrict -xautopar -xloopinfo | 1343.303243 |
約 60% と、予想外に大きく向上しました。が、元々このプログラムはループ演算の性能評価に特化したものなので、ループの並列化が全体のパフォーマンスに大きく影響を及ぼすことは当たり前だとも言えます。一般的にこのくらい向上するわけでは決してないと思います。
プログラム全体にわたって効率よく最適化するには、OpenMP による並列化やマルチスレッド化を自ら行うのが良いのですが、手元のプログラムにまったく変更を加えることなく、マルチコア/マルチソケット環境の恩恵を得るには良い選択肢だと思います。
また、今回の gosa の演算をやめてしまうような変更は全然ダメですが、手元のプログラムでループ間の変数の依存を比較的簡単に調整できそうな場合は、ちょっとした変更でパフォーマンスの向上が見込めるかもしれません。
参考:
Posted by keiichio
( 3月 25日 2008年, 02:47:21 午後 JST )
Permalink
投稿されたコメント [0]