exponenta event banner

Обработка шаблона на GPU

В этом примере показано, как создавать ядра CUDA ® для операций типа трафарета, реализуя «Game of Life» Джона Х. Конвея.

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

  • Ячейки расположены в 2-D сетке.

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

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

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

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

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

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

Предварительные условия для сторонних производителей

Необходимый

В этом примере создается CUDA MEX со следующими требованиями сторонних производителей.

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

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

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

Проверка среды графического процессора

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

Преобразование игры жизни для генерации кода GPU

Просмотр расчетов в updateGrid очевидно, что одни и те же операции применяются в каждом местоположении сетки независимо. Однако каждая ячейка должна знать о своих восьми соседях. Измененный gameoflife_stencil.m функция использует gpucoder.stencilKernel pragma для вычисления области 3 на 3 вокруг каждой ячейки. Графический процессор 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


Создание CUDA MEX для функции

Создание CUDA MEX для 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');

См. также

Функции

Объекты

Связанные темы