このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。
GPU でのステンシル処理
この例では、John H. Conway の "ライフ ゲーム" を実装することにより、ステンシル タイプ演算の CUDA® カーネルを生成する方法を説明します。
"ライフ ゲーム" はプレーヤーのいない "セル オートマトン" ゲームであり、このゲームは四角形グリッド ("世界") のセルの集合 ("人口") で構成されます。セルは "世代" とも呼ばれる離散時間ステップで進化します。セルには一連の数学規則が適用され、その隣接するセルによってそれぞれの生、死、および繁殖が制御されます。この "ライフ ゲーム" の実装は、電子書籍『Experiments in MATLAB』(Cleve Moler 著) で提供されている例に基づきます。その実装は、次の規則に従います。
セルは 2 次元グリッドに配置される。
各ステップで、最近傍の 8 つのセルの生命力によって各セルの運命が決定される。
任意のセルが、ちょうど 3 つの生きているセルと隣接している場合、次のステップで生命を宿す。
生きているセルが、ちょうど 2 つの生きているセルと隣接している場合、次のステップでも生存する。
他のセルはすべて (隣接セルが 3 つを超えるセルも含めて) 次のステップで死ぬか、空のままになる。
以下に、セルの更新のされ方の例をいくつか示します。
多くの配列演算は "ステンシル" 演算として表現できます。そこでは、出力配列の各要素が入力配列の小さな領域によって決まります。この例におけるステンシルは、各セルを囲む 3 行 3 列の領域になります。有限差分、畳み込み、メディアン フィルター処理、および有限要素法は、ステンシル処理で実行できる他の操作の例です。
サードパーティの必要条件
必須
この例では、CUDA MEX を生成します。以下のサードパーティ要件が適用されます。
CUDA 対応 NVIDIA® GPU および互換性のあるドライバー。
オプション
スタティック ライブラリ、ダイナミック ライブラリ、または実行可能ファイルなどの MEX 以外のビルドについて、この例では以下の要件も適用されます。
NVIDIA Toolkit。
コンパイラおよびライブラリの環境変数。詳細については、サードパーティ ハードウェアと前提条件となる製品の設定を参照してください。
GPU 環境の検証
この例を実行するのに必要なコンパイラおよびライブラリが正しく設定されていることを検証するために、関数coder.checkGpuInstall
を使用します。
envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);
ランダムな初期人口の生成
このゲームにはプレーヤーがいないため、ゲームの展開は初期状態で決まります。この例では、セルの初期人口が 2 次元グリッド上に作成されており、約 25% の位置が生存しています。
gridSize = 500; numGenerations = 100; initialGrid = (rand(gridSize,gridSize) > .75); % Draw the initial grid imagesc(initialGrid); colormap([1 1 1;0 0.5 0]); title('Initial Grid');
ライフ ゲームのプレイ
関数 gameoflife_orig.m
は、"ライフ ゲーム" を完全にベクトル化した実装です。この関数は、1 世代につき 1 回、グリッド上のすべてのセルを更新します。
type gameoflife_orig
%% MATLAB vectorized implementation function grid = gameoflife_orig(initialGrid) % Copyright 2016-2019 The MathWorks, Inc. numGenerations = 100; grid = initialGrid; [gridSize,~] = size(initialGrid); % Loop through each generation updating the grid and displaying it. for generation = 1:numGenerations grid = updateGrid(grid, gridSize); imagesc(grid); colormap([1 1 1;0 0.5 0]); title(['Grid at Iteration ',num2str(generation)]); drawnow; end function X = updateGrid(X, N) % Index vectors increase or decrease the centered index by one % thereby accessing neighbors to the left,right,up, and down. p = [1 1:N-1]; q = [2:N N]; % Count how many of the eight neighbors are alive. neighbors = X(:,p) + X(:,q) + X(p,:) + X(q,:) + ... X(p,p) + X(q,q) + X(p,q) + X(q,p); % A live cell with two live neighbors, or any cell with % three live neighbors, is alive at the next step. X = (X & (neighbors == 2)) | (neighbors == 3); end end
初期人口を指定して関数 gameoflife_orig
を呼び出すことで、ゲームをプレイします。ゲームは 100 世代を反復し、世代ごとに人口を表示します。
gameoflife_orig(initialGrid);
GPU コード生成用のライフ ゲームの変換
関数 updateGrid
の計算を見ると、同じ演算が各グリッド位置に個別に適用されていることがわかります。しかし、各セルはその隣接セルについて把握していなければなりません。変更済みの関数 gameoflife_stencil.m
では、stencilfun
プラグマを使用して各セルを囲む 3 行 3 列の領域を計算しています。GPU Coder™ のステンシル カーネルの実装では、各スレッドでグリッドの 1 つの要素を計算し、共有メモリを使用することで、メモリ帯域幅とデータ局所性を改善しています。
type gameoflife_stencil
function grid = gameoflife_stencil(initialGrid) %#codegen % Copyright 2016-2019 The MathWorks, Inc. numGenerations = 100; grid = initialGrid; % Loop through each generation updating the grid. for generation = 1:numGenerations grid = stencilfun(@updateElem, grid, [3,3], Shape='same'); end end function X = updateElem(window) neighbors = window(1,1) + window(1,2) + window(1,3) ... + window(2,1) + window(2,3) ... + window(3,1) + window(3,2) + window(3,3); X = (window(2,2) & (neighbors == 2)) | (neighbors == 3); end
関数の CUDA MEX の生成
関数 gameoflife_stencil
の CUDA MEX を生成するには、GPU コード構成オブジェクトを作成し、codegen
コマンドを使用します。
cfg = coder.gpuConfig('mex'); codegen -config cfg -args {initialGrid} gameoflife_stencil
Code generation successful.
MEX 関数の実行
ランダムな初期人口を指定して、生成された gameoflife_stencil_mex
を実行します。
gridGPU = gameoflife_stencil_mex(initialGrid); % Draw the grid after 100 generations imagesc(gridGPU); colormap([1 1 1;0 0.5 0]); title('Final Grid - CUDA MEX');
参考
関数
codegen
|coder.gpu.kernel
|coder.gpu.kernelfun
|gpucoder.matrixMatrixKernel
|coder.gpu.constantMemory
|stencilfun
|coder.checkGpuInstall