В этом примере показано, как протестировать решения в сравнении с эталоном линейной системы путем генерации кода графического процессора. Используйте матричное левое деление, также известное как mldivide
или оператор обратной косой черты (\), чтобы решить систему линейных уравнений для (то есть, вычисляют).
CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.5 или выше.
NVIDIA инструментарий CUDA и драйвер.
Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Сторонние продукты. Для подготовки переменных окружения смотрите Подготовку Необходимых как условие продуктов.
Чтобы проверить, что компиляторы и библиотеки, необходимые для выполнения этого примера, настраиваются правильно, используйте coder.checkGpuInstall
функция.
envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);
0Choose соответствующий матричный размер для расчетов путем определения суммы системной памяти в Гбайт, доступном для центрального процессора и графического процессора. Значение по умолчанию базируется только на объеме памяти, доступном на графическом процессоре. Можно задать значение, которое подходит для системы.
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.011606 sec Time on GPU: 0.012707 sec Size : 4096 Creating a matrix of size 4096-by-4096. Time on CPU: 0.251755 sec Time on GPU: 0.061536 sec Size : 7168 Creating a matrix of size 7168-by-7168. Time on CPU: 0.879068 sec Time on GPU: 0.137024 sec Size : 10240 Creating a matrix of size 10240-by-10240. Time on CPU: 2.161492 sec Time on GPU: 0.274395 sec Size : 13312 Creating a matrix of size 13312-by-13312. Time on CPU: 4.235362 sec Time on GPU: 0.480827 sec Size : 16384 Creating a matrix of size 16384-by-16384. Time on CPU: 8.280482 sec Time on GPU: 0.761206 sec Size : 19456 Creating a matrix of size 19456-by-19456. Time on CPU: 11.975437 sec Time on GPU: 1.158090 sec Size : 22528 Creating a matrix of size 22528-by-22528. Time on CPU: 18.745547 sec Time on GPU: 1.673389 sec Size : 25600 Creating a matrix of size 25600-by-25600. Time on CPU: 27.993778 sec Time on GPU: 2.309414 sec Starting benchmarks with 7 different double-precision matrices of sizes ranging from 1024-by-1024 to 19456-by-19456. Size : 1024 Creating a matrix of size 1024-by-1024. Time on CPU: 0.026676 sec Time on GPU: 0.020420 sec Size : 4096 Creating a matrix of size 4096-by-4096. Time on CPU: 0.438594 sec Time on GPU: 0.175739 sec Size : 7168 Creating a matrix of size 7168-by-7168. Time on CPU: 1.653028 sec Time on GPU: 0.770956 sec Size : 10240 Creating a matrix of size 10240-by-10240. Time on CPU: 4.596732 sec Time on GPU: 2.075814 sec Size : 13312 Creating a matrix of size 13312-by-13312. Time on CPU: 8.706927 sec Time on GPU: 4.432075 sec Size : 16384 Creating a matrix of size 16384-by-16384. Time on CPU: 20.101956 sec Time on GPU: 8.063678 sec Size : 19456 Creating a matrix of size 19456-by-19456. Time on CPU: 27.399002 sec Time on GPU: 13.432378 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;