特にSOに投稿した他の唯一の質問に基づいて、これは宿題の質問かもしれないと思います。
- スレッド/ブロック/グリッドの数は? この質問に対する答えは、スレッド戦略によって異なります。各スレッドは何をしますか? 画像処理や行列乗算など、大量の出力を生成する問題の一般的なスレッド戦略は、1 つの出力ポイントを作成する作業を実行する各スレッドを割り当てることです。しかし、この問題は少数の出力値 (2 のようです) しか生成せず、リダクション、ストリーム圧縮、およびヒストグラムを含む問題のカテゴリに含まれます。これらの問題は多くの場合、2 つのステップ (おそらく 2 つのカーネル...) で解決され、一般的なスレッド戦略 (少なくとも最初のステップまたはカーネルの場合) は、各入力ポイントに 1 つのスレッドを割り当てることです。ただし、以下の 2 に対する私の回答も参照してください。必要なスレッド数がわかったら、ブロックごとに 256 や 512 などのスレッド数を選択するのが一般的です (間違いなく 2 の累乗を使用します)。
- 二次元か一次元か?あなたの問題は本質的に2Dではないため、スレッドの1Dグリッドは妥当な出発点です。ただし、スレッドの 1D グリッドでは、グリッドで作成できるスレッドの最大数は、使用している GPU の最大グリッド X ディメンションにブロックあたりのスレッド数を掛けた値に制限されます。これらの数値は通常 65535 や 1024 のようなものであるため、入力ポイントの約 64M 要素の後、スレッドが不足します。この時点で、2D グリッド構造を使用するように変換することは難しくありません。これにより、可能なスレッドの数が、GPU が一度に処理できるサイズよりも大きくなります。ただし、スレッドブロックの 2D グリッドに切り替えるのではなく、スレッドブロックの 1D グリッドを保持しつつ、各スレッドに複数の入力ポイント/要素を処理させるという方法もあります。おそらくカーネル コードでループを使用します。たとえば、ループが最大 512 個の要素を処理できる場合、65535x1024x512 で問題のサイズをカバーできます。これは、この種の問題に対する便利なスレッド戦略でもあります。スレッドは、他のスレッドと干渉したり同期したりすることなく、作成した中間結果 (これまでの 1 と 0 のカウント) のローカル コピーを保持できるからです。
- 上記に基づく私の提案は、単一のスレッドがループを実行し、ループの各パスが要素を見て、1 と 0 のカウントを含むローカル変数を更新することです。これは、2 部構成のアルゴリズムの最初の部分になります。2 番目の部分では、これらの中間結果を収集する必要があります。第 2 部が第 1 部の結果をどのように収集するかについて、少し考えてみてください。たとえば、カーネルの完了時に、中間結果をグローバル メモリに保存したい場合があります。
- ワープ/タイリング? ワープとは、実行のためにスレッドを 32 スレッド単位にグループ化することを指します。これは自動的に行われます。グローバル メモリから値を読み取る (またはグローバル メモリに値を書き込む) ときに、各スレッドが連続した連続したブロックで読み取る (または書き込む) ように、アルゴリズムを調整する必要があります。つまり、スレッド 0 は位置 0 から読み取り、スレッド 1 は次の位置から読み取る、などです。スレッドで異常なことを何もしなければ、これは多かれ少なかれ自動的に行われます。cudaMalloc によって作成されたデータ ストレージは適切に整列されます。配列のインデックス戦略が a[thread_number] のようなものである場合、ワープ全体で整列され結合されたアクセスが得られます。これは、GPU から十分な速度を得るために推奨されます。タイリングとは、局所性を強調するためにデータ アクセスを整理するプロセスを指します。これは通常、キャッシュに依存するアーキテクチャに役立ちます。メモリの合体を適切に行うと、キャッシュにあまり依存しなくなります。
時間を割くことができれば、CUDA C プログラミング ガイドは非常に読みやすいドキュメントであり、優れた GPU プログラミングに必要な基本概念を紹介します。また、nvidia の Web サイトにはウェビナーがあり、ここで重要な資料を約 2 時間でカバーできます。また、スラストは最小限のコーディング作業 (C++) でこのような問題を便利に処理できますが、それはあなたが今やろうとしていることの範囲外だと思います.