Этот пример решает системы линейных уравнений Ax = B
для x
при помощи библиотеки cuSOLVER. Матрицы A
и B
должно иметь одинаковое число строк. Если A
является скаляром, тогда A\B
эквивалентно A.\B
. Если A
является квадратной n-на-n матрицей и B
- матрица с n строками, затем x = A\B
является решением уравнения A*x = B
, если он существует. MATLAB® реализация backslash
является:
function [x] = backslash(A,b) if (isscalar(A)) x = coder.nullcopy(zeros(size(b))); else x = coder.nullcopy(zeros(size(A,2),size(b,2))); end x = A\b; end
backslash
для создания ядраДля GPU Coder™ не требуется специальной прагмы, чтобы генерировать вызовы библиотек. Так же, как и прежде, существует два способа сгенерировать CUDA® ядра - coder.gpu.kernelfun
и coder.gpu.kernel
. В этом примере мы используем coder.gpu.kernelfun
прагма для генерации ядер CUDA. Измененный backslash
функция является:
function [x] = backslash(A,b) %#codegen if (isscalar(A)) x = coder.nullcopy(zeros(size(b))); else x = coder.nullcopy(zeros(size(A,2),size(b,2))); end coder.gpu.kernelfun() x = A\b; end
Примечание
Минимальный размер требуется на входных данных для замены математических операторов и функций на реализации библиотеки cuSOLVER. Минимальный порог - 128 элементов.
Когда вы генерируете код CUDA, GPU Coder создает вызовы функций, чтобы инициализировать библиотеку cuSOLVER, выполнить mldivide
операции и релиз аппаратных ресурсов, используемых библиотекой cuSOLVER. Фрагмент сгенерированного кода CUDA:
cusolverEnsureInitialization(); /* Copyright 2017 The MathWorks, Inc. */ cudaMemcpy(b_gpu_A, A, 1152UL, cudaMemcpyHostToDevice); blackslash_kernel1<<<dim3(1U, 1U, 1U), dim3(160U, 1U, 1U)>>>(b_gpu_A,gpu_A); cudaMemcpy(b_A, gpu_A, 1152UL, cudaMemcpyDeviceToHost); cusolverDnDgetrf_bufferSize(cusolverGlobalHandle, 12, 12, &gpu_A[0], 12, &cusolverWorkspaceReq); cusolverWorkspaceTypeSize = 8; cusolverInitWorkspace(); cudaMemcpy(gpu_A, b_A, 1152UL, cudaMemcpyHostToDevice); cusolverDnDgetrf(cusolverGlobalHandle, 12, 12, &gpu_A[0], 12, (real_T *) cusolverWorkspaceBuff, &gpu_ipiv_t[0], gpu_info_t); A_dirtyOnGpu = true; cudaMemcpy(&info_t, gpu_info_t, 4UL, cudaMemcpyDeviceToHost);
Чтобы инициализировать библиотеку cuSOLVER и создать указатель на контекст библиотеки cuSOLVER, функция cusolversEnsureInitialization()
вызывает cusolverDnCreate()
cuSOLVER API. Он выделяет аппаратные ресурсы на хосте и устройстве.
static void cusolverEnsureInitialization(void) { if (cusolverGlobalHandle == NULL) { cusolverDnCreate(&cuSolverGlobalHandle); } }
backslash_kernel1
нуль заполняет матрицу A
. Это ядро запускается с одним блоком из 512 потоков.
static __global__ __launch_bounds__(160, 1) void backslash_kernel1(const real_T * A, real_T *b_A) { int32_T threadId; ; ; threadId = (int32_T)(((gridDim.x * gridDim.y * blockIdx.z + gridDim.x * blockIdx.y) + blockIdx.x) * (blockDim.x * blockDim.y * blockDim.z) + (int32_T)((threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x) + threadIdx.x)); if (!(threadId >= 144)) { /* Copyright 2017 The MathWorks, Inc. */ b_A[threadId] = A[threadId]; } }
Вызовы в cudaMemcpy
передавать матрицу A
от хоста к устройству. Функция cusolverDnDgetrf
вычисляет LU-факторизацию m × n матрицы:
P*A = L*U
где A - матрица m × n, P - матрица сочетаний, L - нижняя треугольная матрица с единичной диагональю, а U - верхняя треугольная матрица.
Для таких функций, как qr
которые имеют только частичную поддержку в cuSOLVER, GPU Coder использует библиотеку LAPACK, где это необходимо. Для MEX-функций генератор кода использует библиотеку LAPACK, которая включена в MATLAB. Для автономного кода генератор кода использует указанную вами библиотеку LAPACK. Чтобы задать библиотеку LAPACK:
В командной строке задайте свои собственные coder.LAPACKCallback
класс, содержащий информацию о библиотеке LAPACK и присвоить ее CustomLAPACKCallback
свойство объекта строения кода.
В приложении GPU Coder установите Custom LAPACK коллбэк в библиотеку LAPACK.
Для примера, чтобы сгенерировать независимый исполняемый файл, можно использовать следующий скрипт генерации кода. Вот myLAPACK
- имя пользовательского coder.LAPACKCallback
класс, содержащий сведения о библиотеке LAPACK.
cfg = coder.gpuConfig('exe'); cfg.CustomLAPACKCallback = 'myLAPACK'; cfg.GenerateExampleMain = 'GenerateCodeAndCompile'; classdef myLAPACK < coder.LAPACKCallback methods (Static) function hn = getHeaderFilename() hn = 'lapacke.h'; end function updateBuildInfo(buildInfo, buildctx) [~,linkLibExt] = buildctx.getStdLibInfo(); cudaPath = getenv('CUDA_PATH'); libPath = 'lib\x64'; buildInfo.addIncludePaths(fullfile(cudaPath,'include')); libName = 'cusolver'; libPath = fullfile(cudaPath,libPath); buildInfo.addLinkObjects([libName linkLibExt], libPath, ... '', true, true); lapackLocation = 'C:\LAPACK\win64'; % specify path to LAPACK libraries includePath = fullfile(lapackLocation,'include'); buildInfo.addIncludePaths(includePath); libPath = fullfile(lapackLocation,'lib'); libName = 'mllapack'; buildInfo.addLinkObjects([libName linkLibExt], libPath, ... '', true, true); buildInfo.addDefines('HAVE_LAPACK_CONFIG_H'); buildInfo.addDefines('LAPACK_COMPLEX_STRUCTURE'); end end end