CUDAではif分岐を使うな?

目次

何の話か

世の中には「CUDAではできるだけif分岐を使わないほうが良い」という言葉を伝言ゲームのように流布している人たちが多いように思えます。
1 Warpのスレッドでif内に入るスレッドとelse内に入るスレッドに分かれたとき、これらは並列に処理されず、片方の処理をもう片方のスレッドが常に待つといういわゆるWarpダイバージェンス問題がその理由かと思います。
これは確かに真なのですが、この問題を「CUDAではできるだけif分岐を使わないほうが良い」で回避するのは思考停止すぎでは?という話です。

if文を適切に使いWarpダイバージェンス問題による性能劣化を軽減する

例えば1 Block中でDeviceメモリからSharedメモリに行列データをコピーすることを考えます。
Sharedメモリの大きさはSMEM_M\(\times\)SMEM_Nで、SMEM_Mblock_sizeの倍数であるとします。
コピーする行列の大きさM\(\times\)Nは定数ではなく、カーネル関数の引数などから計算される値とします。
ちょうどSharedメモリブロッキングを用いて大きな行列積を計算する場合を考えています。
このとき単純に書いたコードがこちらです。

初めのループを列をインクリメントする方向に回し、2つ目のループで行に関して回します。
行にthreadを割り当てコピーしていきます。
このコードでは分岐が3つ見えますよね。
imの領域外判定をするif文が1つと、forが2つで計3つです。
行列のコピーなので3つくらい条件判定が出てくるのは仕方ないですよね。
領域外判定の部分ではWarpダイバージェンスが起こる可能性があります。
しかしif文を逆に増やすことでこのダイバージェンスは減らせます。
それが次のコード。

このコードではとりあえずM == SMEM_Mという分岐で処理内容を分けます。
すると真の場合のループでは領域外判定が不要になります。
つまり、if文を適切に使うことによりWarpダイバージェンスが入り込む処理とそうではない処理を分離することができます。
Sharedメモリブロッキングのコードなどではとても有効な手法です。

おわりに

思考停止で分岐を排除したほうが良いなんて言っていないで、もう少しスレッドの気持ちになりましょうねという話でした。
ちゃんとWarpダイバージェンスを減らすことをしないとです。

カテゴリー:CUDA
記事作成日:2021-08-06