Conwayの人生のゲームのCuda Kernel
-
09-10-2019 - |
質問
n反復用のPXQマトリックスのConwayのGOLの実行で行われる移行の数を計算しようとしています。たとえば、初期状態が1つの責任者である1回の反復が与えられます(以下のように)。 5つの移行(2つの出生、1つの生存、2つの死亡者による2つの死亡)があります。私はすでにこれを機能させていますが、このロジックをCUDAを使用して実行するように変換したいと思います。以下は私がCudaに移植したいものです。
コード:
static void gol() // call this iterations x's
{
int[] tempGrid = new int[rows * cols]; // grid holds init conditions
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
tempGrid[i * cols + j] = grid[i * cols + j];
}
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
int numNeighbors = neighbors(i, j); // finds # of neighbors
if (grid[i * cols + j] == 1 && numNeighbors > 3)
{
tempGrid[i * cols + j] = 0;
overcrowding++;
}
else if (grid[i * cols + j] == 1 && numNeighbors < 2)
{
tempGrid[i * cols + j] = 0;
underpopulation++;
}
else if (grid[i * cols + j] == 1 && numNeighbors > 1)
{
tempGrid[i * cols + j] = 1;
survival++;
}
else if (grid[i * cols + j] == 0 && numNeighbors == 3)
{
tempGrid[i * cols + j] = 1;
birth++;
}
}
}
grid = tempGrid;
}
解決
メインの減速はメインメモリアクセスになります。そこで、利用可能なハードウェアに基づいて、大型スレッドブロックサイズを選択することをお勧めします。 256(16x16)は、クロスハードウェアの互換性に適しています。これらのスレッドブロックのそれぞれは、ボードのわずかに小さいセクションの結果を計算します。16x16を使用した場合、1つの要素の境界があるため、ボードの14x14セクションの結果を計算します。 (16x16ブロックを使用して16x16チャンクではなく14x14チャンクを計算する理由は、メモリ読み取りの合体です。)
ボードを(たとえば)14x14チャンクに分割します。それがあなたのグリッドです(あなたが適切だと思うが整理されていますが、おそらくそうです board_width / 14
, board_height / 14
.
カーネル内で、各スレッドにその要素を共有メモリにロードさせます。次に、同期します。次に、中央の14x14要素に新しい値を計算して(共有メモリに保存されている値を使用)、グローバルメモリに書き戻します。共有メモリの使用は、グローバルな読み取りと書き込みを最小限に抑えるのに役立ちます。これは、スレッドブロックサイズをできるだけ大きくする理由でもあります。エッジとコーナーは、グローバルメモリアクセスを「無駄に」します。
他のヒント
これが1つの方法を進めることができます。
- 各スレッドは、グリッドの1つの要素の計算を作成します
- 各スレッドは、最初にメイングリッドから共有メモリに1つの要素をロードします
- スレッドブロックの端にあるスレッドも境界要素をロードする必要があります
- 各スレッドは、共有メモリの内容に基づいて生存計算を行うことができます
- 次に、各スレッドは結果をメインメモリに書き戻します