OS XのOpenCL - その21 [OS XのOpenCL]
ということで、AppleによるOS X用のOpenCLプログラミングガイドの抄訳はこれでおしまいにする。まだ部分的に残ってるけど基本的なところはわかった。とくに最後のパフォーマンスチューニングのところは非常に濃かった(最初のナイーブなバージョンより数十倍から百倍速くなる、ということは書き方によってそれだけの差が出てしまうということで、注意しないといけない)が、Appleのガイドによくある、一見冗長なほど親切なくせに、明らかに説明不足と思われるところがある文書になっている(文章構造もおかしなところがあるし)。
ところで、この文書はプログラミングガイドなので、ハードウェアに関する説明が補足程度にしかない。しかし前提となるハードウェアがわかっていないと理解できないところがある。僕はハードウェアは苦手でよくわかっていない。ということで別途勉強することにした....
またざっくり言って、CUDAというプロプライエタリな技術を標準規格にしたい、というのがOpenCLの動機のようで、CUDAの後追いをしておいしいところを頂こう、というつもりのように思える。でも、CUDAの本家であるNVIDIAはOpenCLをサポートしている。NVIDIAから見ればCUDAと競合するわけではなくて、OpenCLはCUDAのクロスプラットフォームなラッパのようなものという認識なんだろう。
ディスクリートGPUというのは単体GPUと言ったりするらしいけど、GPU自身がメモリを持ってPCIバスなんかでCPU側のシステムとつながっているものを指している。
統合型GPUというのはメモリコントローラ(あるいはノースブリッジ)がGPUの機能を含んでいる(あるいはGPUチップと直接つながっている)ものを言うらしい。メモリはCPUとシェアする、あるいは共通で使う部分を専用に持っているようなものであるらしい。
なんでこういう分類を考えるか、というとOS XでのOpenCLのAPIはメモリがどこにあるかをプログラマから隠蔽するようになっているのに、実行効率は処理すべきデータがどこにあるかで違ってくるからである(実行効率を目指してるくせに効率に影響するパラメータを隠蔽するというのはおかしい。どう書いても効率のいいコードになるというのならわかるけど、少なくとも現状ではチューニングが必要。それともいずれそのうちGPUはチューニングなんか不要になる、と考えているのか)。具体的に言えばGPUは自分がアクセスできるメモリ上のデータしか処理できなくて、ディスクリートGPUの場合、CPUが持っているデータをいったんGPUのメモリにコピーし、結果をまたCPU側のメモリにコピーし直す、ということが必要であるのに対して、統合型GPU(そしてもちろんCPUの場合にも)ではそのコピー操作が不要となる。
OpenCLには明示的にデータをコピーする関数があるが、専用のバッファオブジェクトを使えば、コピーが必要な場合にはコピーされるし、CPUとGPUで共通のメモリ領域を持っているなら、その領域が使われるらしい。
データの互換性があるのは当たり前のように思えるけど、GPUには専用のデータ型がいろいろ、例えば半精度浮動小数点数(16ビット幅の浮動小数点数)なんていうものがあるらしい。CPUにデータを書き戻すのではなく、計算した後OpenGLに渡して表示するだけ、とかいう場合ならCPUとの互換性はあまり問題にならない可能性がある。でもまあそれは特殊な例だろう。
また、分岐命令に関してはあとでちょっとまとめることにする。
こんなものがどうやって実現されているか、というのは、なかなかわかりやすい資料が見つからない。メモリや計算ユニットが幾つかの階層に分かれていて、メモリに対するアクセス調停とコヒーレンシの調整をハードウェアレベルでやっているらしい。ものすごく複雑で難しい。
たとえば汎用の数値計算ではいちいち誤差の評価をしたくないので、桁落ちなんかを起こさないようにして、あとは全部倍精度で計算する、ということが多い。実際には単精度では10進で7桁、倍精度では16桁近く精度(有効桁数)があるので計算結果を表現するには十分であることが多いけど、ちゃんと精度追跡を行わなければ桁落ちなどで簡単に半分以下になってしまう(逆に精度追跡を真面目にやれば単精度で十分というアプリケーションがほとんどのはずなんだけど、まあそれは大変)。
ということはOpenCLでSIMD計算が簡単に速くできる、と言っても倍精度を使った数値計算がぶりぶりできるというわけではない。最近は汎用並列数値計算専用のGPGPUもあるらしいので、そのうち単精度倍精度を気にしなくてもよくなるかもしれない。
ところで、この文書はプログラミングガイドなので、ハードウェアに関する説明が補足程度にしかない。しかし前提となるハードウェアがわかっていないと理解できないところがある。僕はハードウェアは苦手でよくわかっていない。ということで別途勉強することにした....
15.1 ハードウェアについて
15.1.1 GPUってなに?
そもそもGPUってどんな構造なのか、というのも実は僕は知らなかった。Wikipediaに詳しく紹介されている。 いろいろな歴史があって今のGPUになったらしい。いわゆるビデオカードの高級なやつ、と思っていたら大間違いのようである。特にGPGPUと呼ばれるデバイスがOpenCLの対象らしい(ちなみにMacは古の昔からグラフィクス表示専用のハードウェアは存在せず、CPUが直接操作する方式を長い間とってきた。そのためパフォーマンスは低かったけど、やろうと思えばソフトウェアでなんでもできるという利点があった。今、それは関係ないけど)。またざっくり言って、CUDAというプロプライエタリな技術を標準規格にしたい、というのがOpenCLの動機のようで、CUDAの後追いをしておいしいところを頂こう、というつもりのように思える。でも、CUDAの本家であるNVIDIAはOpenCLをサポートしている。NVIDIAから見ればCUDAと競合するわけではなくて、OpenCLはCUDAのクロスプラットフォームなラッパのようなものという認識なんだろう。
15.1.2 OpenCLが対象にするデバイス
そもそもOpenCLが対象にするデバイスというのは- CPU
- ディスクリートGPU
- 統合型GPU
ディスクリートGPUというのは単体GPUと言ったりするらしいけど、GPU自身がメモリを持ってPCIバスなんかでCPU側のシステムとつながっているものを指している。
統合型GPUというのはメモリコントローラ(あるいはノースブリッジ)がGPUの機能を含んでいる(あるいはGPUチップと直接つながっている)ものを言うらしい。メモリはCPUとシェアする、あるいは共通で使う部分を専用に持っているようなものであるらしい。
なんでこういう分類を考えるか、というとOS XでのOpenCLのAPIはメモリがどこにあるかをプログラマから隠蔽するようになっているのに、実行効率は処理すべきデータがどこにあるかで違ってくるからである(実行効率を目指してるくせに効率に影響するパラメータを隠蔽するというのはおかしい。どう書いても効率のいいコードになるというのならわかるけど、少なくとも現状ではチューニングが必要。それともいずれそのうちGPUはチューニングなんか不要になる、と考えているのか)。具体的に言えばGPUは自分がアクセスできるメモリ上のデータしか処理できなくて、ディスクリートGPUの場合、CPUが持っているデータをいったんGPUのメモリにコピーし、結果をまたCPU側のメモリにコピーし直す、ということが必要であるのに対して、統合型GPU(そしてもちろんCPUの場合にも)ではそのコピー操作が不要となる。
OpenCLには明示的にデータをコピーする関数があるが、専用のバッファオブジェクトを使えば、コピーが必要な場合にはコピーされるし、CPUとGPUで共通のメモリ領域を持っているなら、その領域が使われるらしい。
15.1.3 OpenCLが期待するデバイスの構造
CPUで計算するならOpenCLである必要はない(Clang/LLVMは特定の書き方をされたコードに対してだけではあるけど、信じられないほど最適化されたオブジェクトを吐く。そこに現状のOpenCLの出番はどう考えてもあり得ない。その点に関してはコンパイラとの関連で、あとでちょっと考える)。GPUを使ってこそOpenCLの意味がある。OpenCLが想定しているGPUとは具体的にはどんなものか、というと- ホストのCPUとデータ型の互換性がある
- プログラマブルである。プログラミングモデルは大まかに言って
- C式が書ける
- 分岐命令を含んでいる(あるいは少なくともシミュレートできる)
- stdlibのような外部のライブラリは基本的にない
- SIMD型の並列計算が可能である。ただし
- CPUのベクタユニットより並列度が高い
- 並列化の単位となる計算エレメントは自分のインデクス(自分が何番目のユニットか)を知っている
- プログラムはインデクスに依存するように書くことができ、それによってそれぞれの計算エレメントが異なるメモリ領域にアクセスしたり異なる処理を行ったりできる
- そのインデクスの値はGPUの外からプログラム可能である(エレメントの論理的な並び方を変更できる)
データの互換性があるのは当たり前のように思えるけど、GPUには専用のデータ型がいろいろ、例えば半精度浮動小数点数(16ビット幅の浮動小数点数)なんていうものがあるらしい。CPUにデータを書き戻すのではなく、計算した後OpenGLに渡して表示するだけ、とかいう場合ならCPUとの互換性はあまり問題にならない可能性がある。でもまあそれは特殊な例だろう。
また、分岐命令に関してはあとでちょっとまとめることにする。
こんなものがどうやって実現されているか、というのは、なかなかわかりやすい資料が見つからない。メモリや計算ユニットが幾つかの階層に分かれていて、メモリに対するアクセス調停とコヒーレンシの調整をハードウェアレベルでやっているらしい。ものすごく複雑で難しい。
15.1.4 計算精度について
GPUはもともと表示のためのものだったので、浮動小数点数は単精度(IEEE754で32ビット幅)だけがサポートされている場合が多いらしい。従って倍精度計算をするようなカーネルを書くと、ハードウェアによっては効率が悪い、あるいは場合によってはCPUよりもスループットが悪い、ということがあるらしい。たとえば汎用の数値計算ではいちいち誤差の評価をしたくないので、桁落ちなんかを起こさないようにして、あとは全部倍精度で計算する、ということが多い。実際には単精度では10進で7桁、倍精度では16桁近く精度(有効桁数)があるので計算結果を表現するには十分であることが多いけど、ちゃんと精度追跡を行わなければ桁落ちなどで簡単に半分以下になってしまう(逆に精度追跡を真面目にやれば単精度で十分というアプリケーションがほとんどのはずなんだけど、まあそれは大変)。
ということはOpenCLでSIMD計算が簡単に速くできる、と言っても倍精度を使った数値計算がぶりぶりできるというわけではない。最近は汎用並列数値計算専用のGPGPUもあるらしいので、そのうち単精度倍精度を気にしなくてもよくなるかもしれない。
2015-08-16 20:50
nice!(0)
コメント(0)
トラックバック(0)
コメント 0