Получите обработку с помощью шаблона на графическом процессоре

В этом примере показано, как сгенерировать ядра CUDA® для операций типа шаблона путем реализации "Игры Жизни" Джоном Х. Конуэем.

"Игра Жизни" является сотовой игрой автомата нулевого проигрывателя, которая состоит из набора ячеек (население) в прямоугольной сетке (вселенная). Ячейки развиваются на шагах дискретного времени, известных как поколения. Набор математических правил применился к ячейкам, и его соседи управляют своей жизнью, смертью и воспроизведением. Эта "Игра Жизни" реализация основана на примере, обеспеченном в электронной книге Эксперименты в MATLAB Кливом Moler. Реализация следует этим правилам:

  • Ячейки располагаются в 2D сетке.

  • На каждом шаге живучесть восьми самых близких соседей каждой ячейки определяет свою судьбу.

  • Любая ячейка точно с тремя живыми соседями оживает на следующем шаге.

  • Живая ячейка точно с двумя живыми соседями остается живой на следующем шаге.

  • Все другие ячейки (включая тех больше чем с тремя соседями) умирают на следующем шаге или остаются пустыми.

Вот некоторые примеры того, как обновляется ячейка.

Много операций над массивами могут быть выражены как операция шаблона, где каждый элемент выходного массива зависит от небольшой области входного массива. Шаблон в этом примере является 3х3 областью вокруг каждой ячейки. Конечные разности, свертка, медианная фильтрация и методы конечных элементов являются примерами других операций, которые может выполнить обработка шаблона.

Предпосылки

  • CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.2 или выше.

  • NVIDIA инструментарий CUDA и драйвер.

  • Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Сторонние продукты. Для подготовки переменных окружения смотрите Подготовку Необходимых как условие продуктов.

Проверьте среду графического процессора

Чтобы проверить, что компиляторы и библиотеки, необходимые для выполнения этого примера, настраиваются правильно, используйте coder.checkGpuInstall функция.

envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

Сгенерируйте случайную начальную генеральную совокупность

Будучи, что игра является нулевым проигрывателем, эволюция игры определяется ее начальным состоянием. В данном примере начальная генеральная совокупность ячеек создается на двумерной сетке приблизительно с 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 является полностью векторизованной реализацией "Игры Жизни". Функция обновляет все ячейки на сетке в одной передаче на их генерацию.

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

Преобразуйте игру жизни для генерации кода графического процессора

Рассмотрение вычислений в updateGrid функция, очевидно, что те же операции применяются в каждом местоположении сетки независимо. Однако каждая ячейка должна знать о своих восьми соседях. Модифицированная функция gameoflife_stencil.m использует gpucoder.stencilKernel прагма, чтобы вычислить 3х3 область вокруг каждой ячейки. Реализация GPU Coder™ ядра шаблона вычисляет один элемент сетки в каждом потоке и использует общую память, чтобы улучшить пропускную способность памяти и местность данных.

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	


Сгенерируйте MEX CUDA для функции

Сгенерировать MEX CUDA для gameoflife_stencil функционируйте, создайте объект настройки графического процессора кода, и затем используйте 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 прагма. Можно использовать метод, который, как показывают в этом примере, реализовывал область значений операций шаблона, включая алгоритмы конечного элемента, свертки и фильтры.

Для просмотра документации необходимо авторизоваться на сайте