ベンダーにとらわれない OpenCL の最適化に関する私のアドバイスは次のとおりです。
メモリアクセス
- GPU のメモリ帯域幅は驚異的ですが、多くのカーネルで最大のボトルネックになることがよくあります。したがって、メモリの読み取りと書き込みを最小限に抑えます。変数に格納できるものを 2 回読み取らないでください。
- (1) に関連して、隣接するカーネルが隣接するメモリ位置にアクセスするようにして、GPU がアクセスを単一の (多くの場合 128 ビット以上) 読み取りまたは書き込みに結合できるようにします。一般に、ナロー アクセスよりもワイド アクセスを優先します (たとえば、4 つのシングル バイト要素を持つデータ構造がある場合、4 つの uchar 読み取りを行う代わりに、単一の uchar4 として読み取ります)。
- 複数の作業項目内で同じ値が使用されるグローバル データがある場合は、共有ローカル メモリを利用して、グローバル メモリから一度だけ読み取るようにします。共有ローカル メモリは、アクセスがはるかに高速です。
- 可能であれば、メモリをインターリーブして計算します。1 つすべてを実行してから、もう 1 つすべてを実行するのではありません。GPU はこれらをオーバーラップするため、そのうちの 1 つが「フリー」になります。
コンピューティング
- double の代わりに float を使用します。彼らははるかに高速です。
- より低い精度を許容できる場合は、通常はより高速な native_ 関数を使用してください。
- GPU をビジー状態に保つのに十分な作業項目を提供します。グローバル ワーク サイズは、少なくとも数千項目である必要がありますが、数万以上であることが望ましいです。1000 を下回ると、コアがアイドル状態になります。
- 分岐は避けてください。ワーク グループ内の作業項目に発散分岐がある場合、GPU は両方のパスを取り、述語を使用して非アクティブ側の書き込みをマスクする必要があります。ワーク グループ内で一貫した分岐は問題ありません。
- 巨大なカーネルは避けてください。ほとんどのコンパイラは、すべての関数呼び出しをインライン化します。GPU の共有レジスタ ストアの量は限られているため、多数のレジスタを使用するカーネルは、処理中のワーク グループの数を制限できます。
ホスト側
- プログラムについても言及しましたが、もう 1 つの重要な側面は、GPU にデータを出し入れすることです。多くの GPU は計算と同時にこれを行うことができますが、必要なときにすべての準備が整っていることを確認するには、個別のコマンド キューとイベントを使用する必要があります。これは困難ですが、シリアル アップロード/計算/ダウンロード サイクルを並列サイクルに変えることができます (C のアップロード、B の計算、および A のダウンロードがすべて同時に発生します)。
- データ転送が時間の大きな部分を占める場合は、ここで役立つ固定メモリ、ゼロコピー転送、およびベンダー固有のメモリ バッファ作成フラグを調べてください。
- コマンド キューが空になり、GPU がアイドル状態になるのを防ぐために、可能であれば clFinish と読み取り/書き込みのブロックを避けてください。
がんばって、楽しんで、ターゲット ハードウェアでベンチマークを行って、最適化がすべてのハードウェアで有効で、一部のハードウェアで後退していないことを確認してください。