В этом примере показано, как протестировать решения в сравнении с эталоном линейной системы путем генерации кода графического процессора. Используйте матричное левое деление, также известное как mldivide
или оператор обратной косой черты (\), чтобы решить систему линейных уравнений A*x = b
для x
(то есть, вычислите x = A\b
).
CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.5 или выше.
NVIDIA инструментарий CUDA и драйвер.
Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Стороннее Оборудование. Для подготовки переменных окружения смотрите Подготовку Необходимых как условие продуктов.
Чтобы проверить, что компиляторы и библиотеки, необходимые для выполнения этого примера, настраиваются правильно, используйте coder.checkGpuInstall
функция.
envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);
Выберите соответствующий матричный размер для расчетов путем определения суммы системной памяти в Гбайт, доступном для центрального процессора и графического процессора. Значение по умолчанию базируется только на объеме памяти, доступном на графическом процессоре. Можно задать значение, которое подходит для системы.
g = gpuDevice; maxMemory = 0.25*g.AvailableMemory/1024^3;
Этот пример тестирует матричного левого деления в сравнении с эталоном (\) включая стоимость передачи данных между центральным процессором и графическим процессором, чтобы получить четкое представление общего времени приложения при использовании GPU Coder™. Время приложения, профилируя не должно включать время, чтобы создать демонстрационные входные данные. genData.m
функция разделяет генерацию тестовых данных от функции точки входа, которая решает линейную систему.
type getData.m
function [A, b] = getData(n, clz) % Copyright 2017-2019 The MathWorks, Inc. fprintf('Creating a matrix of size %d-by-%d.\n', n, n); A = rand(n, n, clz) + 100*eye(n, n, clz); b = rand(n, 1, clz); end
backslash.m
функция точки входа инкапсулирует (\) операция, для которой вы хотите сгенерировать код.
type backslash.m
function [x] = backslash(A,b) %#codegen % Copyright 2017-2019 The MathWorks, Inc. coder.gpu.kernelfun(); x = A\b; end
Создайте функцию, чтобы сгенерировать MEX-функцию графического процессора на основе конкретного размера входных данных.
type genGpuCode.m
function [] = genGpuCode(A, b) % Copyright 2017-2019 The MathWorks, Inc. cfg = coder.gpuConfig('mex'); evalc('codegen -config cfg -args {A,b} backslash'); end
Эффективность параллельных алгоритмов, которые решают линейную систему, зависит значительно от матричного размера. Этот пример сравнивает эффективность алгоритма для различных матричных размеров.
% Declare the matrix sizes to be a multiple of 1024. sizeLimit = inf; if ispc sizeLimit = double(intmax('int32')); end maxSizeSingle = min(floor(sqrt(maxMemory*1024^3/4)),floor(sqrt(sizeLimit/4))); maxSizeDouble = min(floor(sqrt(maxMemory*1024^3/8)),floor(sqrt(sizeLimit/8))); step = 1024; if maxSizeDouble/step >= 10 step = step*floor(maxSizeDouble/(5*step)); end sizeSingle = 1024:step:maxSizeSingle; sizeDouble = 1024:step:maxSizeDouble; numReps = 5;
Используйте общее прошедшее время в качестве меры эффективности, потому что это позволяет вам сравнить эффективность алгоритма для различных матричных размеров. Учитывая матричный размер, функция сравнительного тестирования создает матричный A
и правая сторона b
однажды, и затем решает A\b
несколько раз, чтобы получить точную меру времени это берет.
type benchFcnMat.m
function time = benchFcnMat(A, b, reps) % Copyright 2017-2019 The MathWorks, Inc. time = inf; % Solve the linear system a few times and take the best run for itr = 1:reps tic; matX = backslash(A, b); tcurr = toc; time = min(tcurr, time); end end
Создайте различную функцию для выполнения кода графического процессора, которое вызывает сгенерированную MEX-функцию графического процессора.
type benchFcnGpu.m
function time = benchFcnGpu(A, b, reps) % Copyright 2017-2019 The MathWorks, Inc. time = inf; gpuX = backslash_mex(A, b); for itr = 1:reps tic; gpuX = backslash_mex(A, b); tcurr = toc; time = min(tcurr, time); end end
Когда вы выполняете сравнительные тесты, расчеты могут занять много времени, чтобы завершиться. Распечатайте некоторую промежуточную информацию о статусе, когда вы завершаете сравнительное тестирование для каждого матричного размера. Инкапсулируйте цикл свыше всех матричных размеров в функции, чтобы протестировать в сравнении с эталоном один - и расчеты с двойной точностью.
Фактические времена выполнения могут варьироваться через различные аппаратные конфигурации. Это сравнительное тестирование было сделано при помощи MATLAB R2020a на машине с 6 ядрами, центральном процессоре Intel® Xeon® на 3.5 ГГц и TITAN Xp NVIDIA графический процессор.
type executeBenchmarks.m
function [timeCPU, timeGPU] = executeBenchmarks(clz, sizes, reps) % Copyright 2017-2019 The MathWorks, Inc. fprintf(['Starting benchmarks with %d different %s-precision ' ... 'matrices of sizes\nranging from %d-by-%d to %d-by-%d.\n'], ... length(sizes), clz, sizes(1), sizes(1), sizes(end), ... sizes(end)); timeGPU = zeros(size(sizes)); timeCPU = zeros(size(sizes)); for i = 1:length(sizes) n = sizes(i); fprintf('Size : %d\n', n); [A, b] = getData(n, clz); genGpuCode(A, b); timeCPU(i) = benchFcnMat(A, b, reps); fprintf('Time on CPU: %f sec\n', timeCPU(i)); timeGPU(i) = benchFcnGpu(A, b, reps); fprintf('Time on GPU: %f sec\n', timeGPU(i)); fprintf('\n'); end end
Выполните сравнительные тесты в одинарной и двойной точности.
[cpu, gpu] = executeBenchmarks('single', sizeSingle, numReps); results.sizeSingle = sizeSingle; results.timeSingleCPU = cpu; results.timeSingleGPU = gpu; [cpu, gpu] = executeBenchmarks('double', sizeDouble, numReps); results.sizeDouble = sizeDouble; results.timeDoubleCPU = cpu; results.timeDoubleGPU = gpu;
Starting benchmarks with 9 different single-precision matrices of sizes ranging from 1024-by-1024 to 25600-by-25600. Size : 1024 Creating a matrix of size 1024-by-1024. Time on CPU: 0.010894 sec Time on GPU: 0.012735 sec Size : 4096 Creating a matrix of size 4096-by-4096. Time on CPU: 0.256912 sec Time on GPU: 0.056594 sec Size : 7168 Creating a matrix of size 7168-by-7168. Time on CPU: 0.859825 sec Time on GPU: 0.138257 sec Size : 10240 Creating a matrix of size 10240-by-10240. Time on CPU: 2.190538 sec Time on GPU: 0.276720 sec Size : 13312 Creating a matrix of size 13312-by-13312. Time on CPU: 4.265689 sec Time on GPU: 0.481420 sec Size : 16384 Creating a matrix of size 16384-by-16384. Time on CPU: 8.167554 sec Time on GPU: 0.771961 sec Size : 19456 Creating a matrix of size 19456-by-19456. Time on CPU: 12.814798 sec Time on GPU: 1.150009 sec Size : 22528 Creating a matrix of size 22528-by-22528. Time on CPU: 19.060859 sec Time on GPU: 1.671082 sec Size : 25600 Creating a matrix of size 25600-by-25600. Time on CPU: 27.237500 sec Time on GPU: 2.318981 sec Starting benchmarks with 6 different double-precision matrices of sizes ranging from 1024-by-1024 to 16384-by-16384. Size : 1024 Creating a matrix of size 1024-by-1024. Time on CPU: 0.025851 sec Time on GPU: 0.021405 sec Size : 4096 Creating a matrix of size 4096-by-4096. Time on CPU: 0.435886 sec Time on GPU: 0.175009 sec Size : 7168 Creating a matrix of size 7168-by-7168. Time on CPU: 1.734958 sec Time on GPU: 0.765464 sec Size : 10240 Creating a matrix of size 10240-by-10240. Time on CPU: 4.240617 sec Time on GPU: 2.081403 sec Size : 13312 Creating a matrix of size 13312-by-13312. Time on CPU: 8.611123 sec Time on GPU: 4.415243 sec Size : 16384 Creating a matrix of size 16384-by-16384. Time on CPU: 19.387437 sec Time on GPU: 8.050974 sec
Постройте результаты и сравните эффективность на центральном процессоре и графическом процессоре для одинарной и двойной точности.
Во-первых, посмотрите на эффективность оператора обратной косой черты в одинарной точности.
fig = figure; ax = axes('parent', fig); plot(ax, results.sizeSingle, results.timeSingleGPU, '-x', ... results.sizeSingle, results.timeSingleCPU, '-o') grid on; legend('GPU', 'CPU', 'Location', 'NorthWest'); title(ax, 'Single-Precision Performance') ylabel(ax, 'Time (s)'); xlabel(ax, 'Matrix Size'); drawnow;
Теперь посмотрите на эффективность оператора обратной косой черты в двойной точности.
fig = figure; ax = axes('parent', fig); plot(ax, results.sizeDouble, results.timeDoubleGPU, '-x', ... results.sizeDouble, results.timeDoubleCPU, '-o') legend('GPU', 'CPU', 'Location', 'NorthWest'); grid on; title(ax, 'Double-Precision Performance') ylabel(ax, 'Time (s)'); xlabel(ax, 'Matrix Size'); drawnow;
Наконец, посмотрите на ускорение оператора обратной косой черты при сравнении графического процессора с центральным процессором.
speedupDouble = results.timeDoubleCPU./results.timeDoubleGPU; speedupSingle = results.timeSingleCPU./results.timeSingleGPU; fig = figure; ax = axes('parent', fig); plot(ax, results.sizeSingle, speedupSingle, '-v', ... results.sizeDouble, speedupDouble, '-*') grid on; legend('Single-precision', 'Double-precision', 'Location', 'SouthEast'); title(ax, 'Speedup of Computations on GPU Compared to CPU'); ylabel(ax, 'Speedup'); xlabel(ax, 'Matrix Size'); drawnow;