Обработка трафаретов на графическом процессоре

Этот пример показывает, как сгенерировать ядра CUDA ® для операций типа трафарета путем реализации «Игры жизни» Джона Х. Конвея.

«Game of Life» - игра в клеточный автомат, состоящая из набора камер (население) в прямоугольной сетке (вселенная). Развитие камер происходит в дискретных временных шагах, известных как поколения. Набор математических правил, применяемых к камерам и их соседям, контролирует их жизнь, смерть и размножение. Эта реализация «Game of Life» основана на примере, приведенном в электронной книге «Эксперименты с MATLAB» Клева Молера. Реализация выполняется в соответствии с этими правилами:

  • Камеры расположены в 2-D сетке.

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

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

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

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

Вот несколько примеров обновления камеры.

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

Необходимые условия для третьих лиц

Необходимый

Этот пример генерирует CUDA MEX и имеет следующие требования к третьим лицам.

  • CUDA включает графический процессор NVIDIA ® и совместимый драйвер.

Дополнительный

Для сборок, не являющихся MEX, таких как статические, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.

Проверьте окружение GPU

Чтобы убедиться, что компиляторы и библиотеки, необходимые для выполнения этого примера, настроены правильно, используйте 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');

Играть в игру жизни

The gameoflife_orig.m функция является полностью векторизованной реализацией «Game of Life». Функция обновляет все камеры сетки за один проход на их генерацию.

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™ ядра трафарета вычисляет один элемент сетки в каждом потоке и использует общую память для улучшения полосы пропускания памяти и локальности данных.

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


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

Чтобы сгенерировать CUDA MEX для gameoflife_stencil function, создайте объект строения кода 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');

См. также

Функции

Объекты

Похожие темы