cuda-samples 深入解析:基于 cuSolverSp 低级 API 的稀疏 QR 分解与线性方程组求解(cuSolverSp_LowlevelQR)
cuda-samples 深入解析基于 cuSolverSp 低级 API 的稀疏 QR 分解与线性方程组求解cuSolverSp_LowlevelQR【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples本文以 NVIDIA cuda-samples 仓库中的 cuSolverSp_LowlevelQR 示例 为对象逐层剖析它如何借助 cuSolverSP 的低级Low-levelAPI 对稀疏矩阵执行 QR 分解、完成A*x b求解并用 cuSPARSE 的 SpMV 校验残差。读完本文你将掌握cusolverSpCreateCsrqrInfo(Host)系列 API 的完整调用链、CPU/GPU 两条求解路径的异同以及如何用 Matrix MarketMM格式的稀疏矩阵驱动这套流程。示例概述为什么需要低级 QR 求解器在 cuSolver 库中求解稀疏线性系统通常有两条路线高级 API如cusolverSpDcsrlsvqr一条函数调用完成分析 → 分解 → 求解 → 残差检查的全部工作接口简单但对过程不可控低级 APILow-level API将求解过程拆解为多个可独立调用的阶段创建 info 结构 → 符号分析 → 工作空间查询 → setup → 数值分解 → 奇异检测 → 求解每个阶段可复用、可调试适合需要精细控制或反复求解同一结构矩阵的场景。本示例正是后者其官方定位为A CUDA Sample that demonstrates QR factorization using cuSolverSPs low level APIs核心概念属于Linear Algebra线性代数与 CUSOLVER Library。完整实现位于 cuSolverSp_LowlevelQR.cpp从源码结构看它把求解过程组织成了 14 个带编号的 printf 步骤清晰展示了低级 API 的每个阶段。环境、架构与依赖支持的软硬件平台根据 README支持的 SM 架构SM 5.0、5.2、5.3、6.0、6.1、7.0、7.2、7.5、8.0、8.6、8.7、8.9、9.0支持的操作系统Linux、Windows支持的 CPU 架构x86_64、armv7l依赖库CUSOLVERQR 分解与求解与 CUSPARSECSR 矩阵描述与 SpMV 残差计算对应仓库根目录 README.md#cusolver 与 README.md#cusparse 中的依赖说明前置条件安装对应平台的 CUDA Toolkit。构建配置CMakeLists.txt 给出了可复现的构建方式cmake_minimum_required(VERSION 3.20) project(cuSolverSp_LowlevelQR LANGUAGES C CXX) find_package(CUDAToolkit REQUIRED) set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120) add_executable(cuSolverSp_LowlevelQR cuSolverSp_LowlevelQR.cpp mmio.c mmio_wrapper.cpp) target_link_libraries(cuSolverSp_LowlevelQR PRIVATE CUDA::cudart CUDA::cublas CUDA::cusolver )要点说明目标由cuSolverSp_LowlevelQR.cpp、mmio.c与mmio_wrapper.cpp三个源文件组成其中后两者负责 Matrix Market 格式的解析与 CSR 转换默认CMAKE_CUDA_ARCHITECTURES覆盖 75Turing到 120Blackwell等主流架构开启ENABLE_CUDA_DEBUG时追加-G以支持 cuda-gdb否则使用-lineinfo保留行号信息通过include_directories(../../../Common)引入 helper_cuda.h 与 helper_cusolver.h 等公共头文件POST_BUILD 阶段会将lap2D_5pt_n32.mtx、lap2D_5pt_n100.mtx、lap3D_7pt_n20.mtx三个数据文件复制到构建输出目录保证运行时能定位默认输入。输入数据Matrix Market 格式与 CSR 转换三个测试矩阵仓库为示例提供了三个稀疏测试矩阵数据文件目录文件规模非零元来源lap2D_5pt_n32.mtx1024 × 10243008带 Dirichlet 边界条件的二维五点拉普拉斯算子lap2D_5pt_n100.mtx10000 × 1000049600同上网格更密lap3D_7pt_n20.mtx8000 × 800054800三维七点拉普拉斯算子以 lap2D_5pt_n32.mtx 为例其头部为%%MatrixMarket matrix coordinate real general % standard 5-point laplace 2D oprator with Dirichlet boundary condition 1024 1024 3008 1 1 4 2 1 -1 33 1 -1 ...第一行 Banner 声明对象为matrix、存储为coordinate稀疏坐标格式、数据类型为real、结构为general第二行1024 1024 3008分别是行数、列数、非零元个数随后每行i j value记录一个三元组。注意这些矩阵是对称的数值上对角占优、主对角为 4、邻接为 -1但声明为 general 后仍需完整展开。mmio_wrapper从 MM 文件到 CSR核心转换逻辑在 mmio_wrapper.cpp 的模板函数loadMMSparseMatrixT_ELEM中它接受元素类型字符d对应 double、是否输出 CSR 格式、是否扩展对称矩阵等参数调用mm_read_mtx_crd实现于 mmio.c声明于 mmio.h读取 COO 三元组若矩阵声明为symmetric/hermitian/skew且开启extendSymMatrix则把三角部分补全为完整矩阵对角元素不复制非对角元素按对称/反对称/共轭规则处理——这是本示例在main中传true的原因用qsort按行主序CSR或列主序CSC对三元组排序自动探测基址若存在下标 0 则为 base-0若出现等于矩阵维度的下标则为 base-1通过compress_index压缩出行指针csrRowPtr生成csrColInd与csrVal调用verify_pattern校验 nnz 一致性、基址合法性、行内列下标单调性防止后续进入 cuSolver 时报错。该模板针对float、double、cuComplex、cuDoubleComplex四种元素类型做了显式实例化文件末尾配合cuGet特化模板完成从 double 到各类型的数值转换——这也是 README 中 Driver API 部分列出cuGet、cuComplex、cuDoubleComplex的原因。命令行参数程序支持三个命令行选项定义于UsageSP/parseCommandLineArguments参数说明-h显示帮助信息并退出-filefilename指定包含 MM 格式矩阵的文件名-devicedevice_id指定运行的 GPU 设备 ID若未提供-file程序会通过sdkFindFilePath(lap2D_5pt_n32.mtx, argv[0])见 helper_cuda.h在可执行文件所在目录查找默认输入文件找不到则报错退出。运行示例./cuSolverSp_LowlevelQR # 使用默认 lap2D_5pt_n32.mtx ./cuSolverSp_LowlevelQR -filelap2D_5pt_n100.mtx # 指定输入矩阵 ./cuSolverSp_LowlevelQR -filelap3D_7pt_n20.mtx -device0求解主流程14 步低级 API 调用链主函数cuSolverSp_LowlevelQR.cpp先做统一的句柄与流初始化cusolverSpCreate、cusparseCreate、cudaStreamCreate并通过cusolverSpSetStream/cusparseSetStream绑定到同一流再用cusparseCreateMatDescr创建 CSR 矩阵描述符descrA根据解析出的baseA设置CUSPARSE_INDEX_BASE_ONE或CUSPARSE_INDEX_BASE_ZERO。随后分别以CPUHost路径步骤 1–8与GPU 路径步骤 9–14各执行一遍完整流程。CPUHost路径步骤 1–8步骤源码位置调用作用1L151–178loadMMSparseMatrixdouble(..., d, true, ..., true)读取 MM 文件并转换为 CSRh_csrValA/h_csrRowPtrA/h_csrColIndA2L220–221cusolverSpCreateCsrqrInfoHost(h_info)创建主机端不透明 info 结构csrqrInfoHost_t3L223–225cusolverSpXcsrqrAnalysisHost符号分析确定 QR 分解后 R 的结构不依赖数值4L227–238cusolverSpDcsrqrBufferInfoHost查询工作空间大小返回size_internal与size_chol随后在 CPU 上malloc(size_chol)5L246–250cusolverSpDcsrqrSetupHostcusolverSpDcsrqrFactorHost载入数值并执行数值分解打印文案沿用了 Cholesky 的A L*L^T实际是 QR6L252–258cusolverSpDcsrqrZeroPivotHost以容差tol 1.e-14检测主元是否为零判定矩阵是否奇异7L260–261cusolverSpDcsrqrSolveHost回代求解A*x b其中b初始化为全 1 向量8L263–341见下节残差验证用 cuSPARSE SpMV 计算r b - A*x并输出范数指标GPU 路径步骤 9–14GPU 路径使用设备端 info 结构csrqrInfo_t与设备端 CSR 数据d_csrValA/d_csrRowPtrA/d_csrColIndA调用序列与 CPU 路径一一对应cusolverSpCreateCsrqrInfo(d_info); // step 9 cusolverSpXcsrqrAnalysis(cusolverSpH, rowsA, colsA, nnzA, descrA, d_csrRowPtrA, d_csrColIndA, d_info); // step 10 符号分析 cusolverSpDcsrqrBufferInfo(cusolverSpH, rowsA, colsA, nnzA, descrA, d_csrValA, d_csrRowPtrA, d_csrColIndA, d_info, size_internal, size_chol); // step 11 工作空间查询 cusolverSpDcsrqrSetup(cusolverSpH, rowsA, colsA, nnzA, descrA, d_csrValA, d_csrRowPtrA, d_csrColIndA, zero, d_info); // step 12 cusolverSpDcsrqrFactor(cusolverSpH, rowsA, colsA, nnzA, NULL, NULL, d_info, buffer_gpu); // step 12 数值分解 cusolverSpDcsrqrZeroPivot(cusolverSpH, d_info, tol, singularity); // step 13 奇异检测 cusolverSpDcsrqrSolve(cusolverSpH, rowsA, colsA, d_b, d_x, d_info, buffer_gpu); // step 14 求解GPU 路径的数值分解阶段实际执行在设备上工作空间buffer_gpu由cudaMalloc分配大小来自 step 11 查询到的size_chol程序会打印GPU buffer size %lld bytes。关键设计分析一次、分解多次符号分析XcsrqrAnalysis与数值分解Factor分离是低级 API 的核心价值对结构相同、数值不同的多个矩阵只需做一次符号分析与工作空间查询即可反复调用Setup Factor Solve这是高级 API 难以直接提供的复用能力。奇异判定与容差程序在分解后调用cusolverSpDcsrqrZeroPivot(Host)检测零主元const double tol 1.e-14; int singularity 0; checkCudaErrors(cusolverSpDcsrqrZeroPivotHost(cusolverSpH, h_info, tol, singularity)); if (0 singularity) { fprintf(stderr, Error: A is not invertible, singularity%d\n, singularity); return 1; }源码注释明确了两点语义singularity -1表示在容差tol下 A 可逆tol决定了奇异的判定条件。任何singularity 0的值都指向首个零主元所在位置程序立即报错退出。三个拉普拉斯测试矩阵都是严格对角占优的 M 矩阵因而都能通过该检查。残差验证cuSPARSE SpMV 通用 API步骤 8 与 GPU 路径的收尾阶段都通过 cuSPARSE 计算残差r b - A*x这是对求解正确性的量化验证。关键点在于它使用了 cuSPARSE 的**通用 APIgeneric API**对象而非旧式描述符cusparseCreateCsr(matA, rowsA, colsA, nnzA, d_csrRowPtrA, d_csrColIndA, d_csrValA, CUSPARSE_INDEX_32I, CUSPARSE_INDEX_32I, baseA ? CUSPARSE_INDEX_BASE_ONE : CUSPARSE_INDEX_BASE_ZERO, CUDA_R_64F); cusparseCreateDnVec(vecx, colsA, d_x, CUDA_R_64F); cusparseCreateDnVec(vecAx, rowsA, d_r, CUDA_R_64F); cusparseSpMV_bufferSize(cusparseH, CUSPARSE_OPERATION_NON_TRANSPOSE, minus_one, matA, vecx, one, vecAx, CUDA_R_64F, CUSPARSE_SPMV_ALG_DEFAULT, bufferSize); cusparseSpMV(cusparseH, CUSPARSE_OPERATION_NON_TRANSPOSE, minus_one, matA, vecx, one, vecAx, CUDA_R_64F, CUSPARSE_SPMV_ALG_DEFAULT, buffer);这里r (-1)*A*x 1*b先查询工作空间再执行。求得的h_r回拷 CPU 后借助 helper_cusolver.h 中的工具函数计算指标vec_norminfL49–56向量无穷范数|r|csr_mat_norminfL77–96按行累加 CSR 矩阵各元素绝对值后取最大值即|A|最终输出相对残差|b - A*x| / (|A|*|x|)CPU 与 GPU 路径各打印一组(CPU) |b - A*x| ...E-14 (CPU) |A| ...E00 (CPU) |x| ...E00 (CPU) |b - A*x|/(|A|*|x|) ...E-14 (GPU) |b - A*x| ...E-14 (GPU) |b - A*x|/(|A|*|x|) ...E-14残差量级达到1e-14左右与双精度求解和tol 1e-14的奇异容差相匹配说明求解结果在浮点精度内满足原方程。资源清理与工程实践主函数结尾对全部句柄与内存做了成对释放可作为编写生产代码的清单参考cusolverSpDestroy、cusparseDestroy、cudaStreamDestroy、cusparseDestroyMatDescr、cusolverSpDestroyCsrqrInfoHost/cusolverSpDestroyCsrqrInfo、cusparseDestroySpMat/cusparseDestroyDnVec以及所有cudaFree/free。这些释放操作与 README 中列出的 CUDA Runtime APIcudaMemcpy、cudaStreamDestroy、cudaFree、cudaMalloc、cudaStreamCreate一一对应。小结cuSolverSp_LowlevelQR 是一个理解 cuSolverSP 低级 API 的理想样本它用同一份 CSR 数据分别跑通 Host 与 Device 两条求解链完整覆盖符号分析 → 工作空间查询 → setup → 数值分解 → 奇异检测 → 求解 → SpMV 残差验证的每个环节并配套了 MM 格式解析、基址探测与结果范数校验等工程细节。开发者可以以此为模板将其中分析一次、多次分解的模式直接复用到 CFD、结构力学、电路仿真等需要反复求解同结构稀疏线性系统的场景中。【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →