Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

GPU でのステンシル処理

この例では、John H. Conway の "ライフ ゲーム" を実装することにより、ステンシル タイプ演算の CUDA® カーネルを生成する方法を説明します。

"ライフ ゲーム" はプレイヤーのいない "セル オートマトン" ゲームであり、このゲームは四角形グリッド ("世界") のセルの集合 ("人口") で構成されます。セルは "世代" とも呼ばれる離散時間ステップで進化します。セルには一連の数学規則が適用され、その隣接するセルによってそれぞれの生、死、および繁殖が制御されます。この "ライフ ゲーム" の実装は、電子書籍『Experiments in MATLAB』(Cleve Moler 著) で提供されている例に基づきます。その実装は、次の規則に従います。

  • セルは 2 次元グリッドに配置される。

  • 各ステップで、最近傍の 8 つのセルの生命力によって各セルの運命が決定される。

  • 任意のセルが、ちょうど 3 つの生きているセルと隣接している場合、次のステップで生命を宿す。

  • 生きているセルが、ちょうど 2 つの生きているセルと隣接している場合、次のステップでも生存する。

  • 他のセルはすべて (隣接セルが 3 つを超えるセルも含めて) 次のステップで死ぬか、空のままになる。

以下に、セルの更新のされ方の例をいくつか示します。

多くの配列演算は "ステンシル" 演算として表現できます。そこでは、出力配列の各要素が入力配列の小さな領域によって決まります。この例におけるステンシルは、各セルを囲む 3 行 3 列の領域になります。有限差分、畳み込み、メディアン フィルター処理、および有限要素法は、ステンシル処理で実行できる他の操作の例です。

サードパーティの必要条件

必須

この例では、CUDA MEX を生成します。以下のサードパーティ要件が適用されます。

  • CUDA 対応 NVIDIA® GPU および互換性のあるドライバー。

オプション

スタティック ライブラリ、ダイナミック ライブラリ、または実行可能ファイルなどの MEX 以外のビルドについて、この例では以下の要件も適用されます。

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 では、gpucoder.stencilKernel プラグマを使用して各セルを囲む 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 = gpucoder.stencilKernel(@updateElem, grid, [3,3], 'same');
end
end

function X = updateElem(window)
    [winH, winW]  = size(window);
    neighbors = 0;
    for ww = 1:winW
        for wh = 1:winH
            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);
        end
    end
    X = (window(2,2) & (neighbors == 2)) | (neighbors == 3);
end	


関数の CUDA MEX の生成

関数 gameoflife_stencil の CUDA MEX を生成するには、GPU コード構成オブジェクトを作成し、codegen コマンドを使用します。

cfg = coder.gpuConfig('mex');
evalc('codegen -config cfg -args {initialGrid}  gameoflife_stencil');

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');

まとめ

シンプルなステンシル演算 (コンウェイの "ライフ ゲーム") の CUDA コードを生成しました。実装は、gpucoder.stencilKernel プラグマを使用して実現しました。この例で示した手法は、有限要素アルゴリズム、畳み込み、フィルターなど、さまざまなステンシル演算の実装に使用できます。