CUDAよりCPUの方が速い?

前の記事GPGPUの概要について調べた内容を書いてみたが、GPGPUについてさらに調べていたらこういう記事を見つけた。
【西川和久の不定期コラム】GPGPU/CUDAで快適動画編集入門 - PC Watch


要するに、クアッドコアのCPUで処理した方が、GPGPU(CUDA)を使うより速かった、という話。なぜそうなるかちょっと考えてみたい。あくまで仮説なので、参考程度にしかならないけれど。


理由としては、処理が並列化に向いていない、最適化が不十分で処理能力を活かせていない、演算以外の部分で時間がかかっている、そもそもCPUの方が処理能力が高い、などが考えられる。

並列化のポイントは?

それぞれについて考えてみる前に、GPUによる並列処理を効率よくこなして性能を発揮させるには何が必要かを考えてみる。
GPUの能力を活かすには、たぶん、単純な処理を延々繰り返すようなアルゴリズムにしないと速度が出ない。GPUのような並列プロセッサは、まったく同じ処理をするプロセッサがたくさん並んでいるという構成をしている。そのたくさんのプロセッサが途切れることなくひたすらに処理をし続けることが、パフォーマンスをあげるのに必要になってくる。なので、たくさんのプロセッサ達に途切れることなくデータを流し込んでやる、ということが処理速度を出すのに一番必要なことと考えられる。


では、どうすれば途切れることなくデータを流し込んで処理を実行させることができるのか。
考えられるのは、メモリへのアクセスをなるべく少なくすること。GPU内の各スレッドを担当するプロセッサには、記憶領域としてレジスタがあるが容量は小さい。スレッドをいくつかまとめたブロックという単位で持っているメモリは、容量はそれなりにあっても、バンクを分けてアクセスしないと並列にアクセスできず遅くなるという制約がある。GPU全体で共有されているグローバルメモリはDDRなので、レジスタや共有メモリとは比較にならないほど遅い。グローバルメモリへのメモリアクセスを発生させると結果が返ってくるまで、そこでプログラムがストップする。つまり、なるべくメモリアクセスを少なくして、一気に処理ができるようなアルゴリズムでプログラミングしないと、並列処理の恩恵が相殺されてしまうと考えられる。


また、メモリアクセスを減らすには、並列処理向けのプログラミングをすることが必要になる。GPUは多数のデータに対して同一の処理を並列に実行する構成になっている。なので、画像や動画のような、処理対象である大きなデータを細かく分割して各スレッドに割り当てていくのが基本的なスタイルになると思われる。どのように分割するのが最も効率がいいかは処理の内容によって異なってくるので、処理にあわせた最適なデータの分割&スレッドへの割り当て設計が必要になる。これができていないと無駄なメモリアクセスを発生させて、処理の実行速度を落とすことになる。


そして、関数呼び出し等のオーバーヘッドを極力発生させないことも効率よい並列化のために必要。GPU上のプログラムは、CPUからは関数として呼び出される。CUDAはこのときのオーバーヘッドが大きいらしい。想像だが、CPU側のプログラムからGPU側のプログラムを呼び出す処理は、GPU側のプログラムメモリへの実行コードのコピーや、GPUのスレッド設定などの処理を行っているんだろうと思う。当然この処理はそれなりの時間がかかるであろう。ということは、CPU側から関数として呼び出されるGPU側プログラムの種類が多いと、オーバーヘッドが大きくなるため、処理全体として時間がかかってしまうと考えられる。

CPUの方が速かった理由

以上のことを踏まえて、先の記事の処理を見てみる。
処理能力については、単純に考えてGPUの方が高い。さっきの記事の例では、CPUは4コアでクロックは2.3GHz。GPUは216コアでクロックは625MHz。演算のしくみが全然違うので単純比較はできないけれど、どう考えても積和演算などの単純計算ならGPUの方が能力は高そうに思う。


次に処理の内容だが、先の記事では動画のエンコードとフィルタ処理(インターレース解除、映像ノイズ除去、輪郭強調、色調補正、音声ノイズ除去、音声ボリューム調整、映像サイズ)を行っている。使っているソフトがどんな処理の仕方をしているか分からないので想像でしかないが、これらの処理が並列化を活かせていないのでは、と考えられる。ソフトの性能が悪いというわけではなくて、並列化してパフォーマンスをあげるのが難しい処理なのではないか、と思う。


フィルタ処理のうち、ノイズ除去や輪郭強調、色補正など、単純に同じ処理を延々繰り返すだけの処理は、並列化に向いている。画像を細かい領域に分割して、領域ごとにスレッドに割り当ててやればいいだけ。


一方、インターレース解除や動画エンコードは画像の解析を含んでいたり、参照範囲が広かったりと、比較的複雑な処理になる。ノイズ除去等の単純な処理も、使っているアルゴリズムによっては画像解析を含んでいるかもしれないので、その場合はやっぱり複雑な処理になる。
複雑な処理、たとえば動画エンコード時の動きベクトル検出のような処理は、画像中の広い範囲を探索しなければならないので、単純に画像をスレッド数に分割して個別に処理するだけではうまくいかないと思われる。探索範囲として隣のブロックまで見なければならないようなことが起こるからだ。同様に、分割された領域内だけで処理が完結できない処理の場合は、分割の仕方に工夫が必要になってくるだろう。どう工夫してもうまく行かない、という場合もあるかもしれない。
そういう処理というのは、たいていスレッド間のデータのやり取りが必要になって、メモリアクセスが多くなりがちなんじゃなかろうか。また、他のスレッドの処理結果を必要とするような処理が含まれていると、その処理待ちに時間をとられるので、並列化している意味が薄れてくる。そういう事態を避ける意味でも、データの分割、スレッドへの割り当て設計が重要になる。
効率のいい処理を実現するのにメモリを必要とするアルゴリズムの場合、並列化で処理速度を上げるのは単純な方法では難しいのだろう。


また、いろいろなフィルタを適用しているので、GPU側の関数を呼び出す頻度も高くなっているのではないかと思われる。そのこともパフォーマンスの低下につながっている可能性がある。


一方CPUの方はキャッシュが大きめなので、メモリをよく使うようなアルゴリズムでもそれなりに効率よく処理ができるのではなかろうか、と予想できる。また、関数呼び出しにかかるコストも、GPUほど大きくはないのだろう。


以上のように、先の記事の処理は並列化してパフォーマンスをあげるのがなかなか難しい処理なのではないか、と考えられる。そのため、CPUで処理するよりもGPUで処理する方が若干時間がかかるという結果になったのだと思われる。

まとめ

GPGPU(CUDA)を使って並列処理のパフォーマンスを発揮させるには、かなり並列化に特化したプログラムを組まなければならないものと予想できる。いままで通りののプログラムの組み方では、CPUで処理するよりもかえって遅くなるということもありうる。そのあたりに並列プロセッサを使いこなせるかどうかの差につながるものがありそうだ。ここを突き詰めていくというのも、並列化プログラミングを行う楽しみのひとつになると思う。




Leadtek Winfast GTX260 Extreme+V3 WFGTX260EX+V3
LEADTEK
売り上げランキング: 7141