В этом примере показано, как сгенерировать ядра 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 для gameoflife_stencil
функционируйте, создайте код, настройка графического процессора кода возражает и использует codegen
функция.
cfg = coder.gpuConfig('mex'); evalc('codegen -config cfg -args {initialGrid} gameoflife_stencil');
Запуститесь сгенерировал 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
прагма. Этот метод, продемонстрированный в этом примере, может использоваться, чтобы реализовать область значений операций шаблона включая алгоритмы конечного элемента, свертки и фильтры.