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

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

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

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

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

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

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

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

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

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

Предпосылки

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

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,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 прагма, чтобы вычислить 3x3 область вокруг каждой ячейки. Реализация GPU Coder™ ядра шаблона, вычисляет один элемент сетки в каждом потоке и использует общую память, чтобы улучшить пропускную способность памяти и местность данных.

type gameoflife_stencil
function grid = gameoflife_stencil(initialGrid) %#codegen

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