ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CUDA 多 GPU 一维卷积示例深度解析:simpleCUFFT_MGPU 与 cuFFT 扩展 API 实战

CUDA 多 GPU 一维卷积示例深度解析:simpleCUFFT_MGPU 与 cuFFT 扩展 API 实战 CUDA 多 GPU 一维卷积示例深度解析simpleCUFFT_MGPU 与 cuFFT 扩展 API 实战【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples导读simpleCUFFT_MGPU是 NVIDIA CUDA Samples 仓库中一个演示cuFFT 库在多 GPU 场景下执行快速一维卷积的官方示例它将一段信号与一个滤波器分别变换到频域、逐点相乘再逆变换回时域且整个过程分布在两块 GPU上完成。通过本文你将掌握 cuFFT 扩展cufftXt的完整多 GPU 调用链cufftCreate→cufftXtSetGPUs→cufftMakePlan1d→cufftXtMalloc→cufftXtMemcpy→cufftXtExecDescriptorC2C理解cudaLibXtDesc库描述符的数据布局并学会用 CUDA 自定义核函数在多 GPU 上完成频域逐点相乘最终与 CPU 参考实现做误差比对。示例概述用 cuFFT 在多个 GPU 上做一维卷积根据 simpleCUFFT_MGPU/README.md 的说明本示例的核心目的是使用 CUFFT 计算某信号与某滤波器的一维卷积——将两者变换到频域逐点相乘再将信号逆变换回时域且整个过程在多个 GPU上完成。这与单 GPU 版的 simpleCUFFT 的区别在于FFT、逆 FFT 及频域乘法全部经由 cuFFT 的扩展接口cufftXt分布在多块 GPU 上执行而不是依赖单设备的cufftExecC2C。关键概念Key Concepts归纳为两项Image Processing图像处理一维卷积是滤波、模糊、边缘检测等图像/信号处理算子的数学基础CUFFT LibraryNVIDIA 官方 FFT 库本示例重点展示其多 GPU 扩展 API。支持环境与构建前提原文档给出的运行环境矩阵如下写作时请以本仓库对应说明为准维度支持范围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、WindowsCPU 架构x86_64、armv7lCUDA APIcudaXtFree、cudaSetDevice、cudaGetDeviceCount、cudaDeviceSynchronize、cudaGetDevicePropertiesCUDA Runtime API依赖库CUFFT前提条件安装对应平台的 CUDA Toolkit并确保 CUFFT 依赖已就绪两点补充说明API 命名细节README 中列出的cudaXtFree在源码中实际以cufftXtFree的形式出现见 simpleCUFFT_MGPU.cu二者同属 cuFFT XT 多 GPU 数据释放接口使用checkCudaErrors统一包裹。错误码支持checkCudaErrors宏定义于 helper_cuda.h能直接解析cufftResult错误枚举见 helper_cuda.h因此示例中对所有 cuFFT 调用采用checkCudaErrors(...)包裹即可获得带文件与行号的错误诊断。构建方式示例的 CMakeLists.txt 展示了标准的多 GPU cuFFT 示例构建方式要求 CMake 3.20 及以上project(simpleCUFFT_MGPU LANGUAGES CUDA)通过find_package(CUDAToolkit REQUIRED)定位 CUDA 工具链默认编译架构为75 80 86 87 89 90 100 110 120通过target_link_libraries(... CUDA::cufft)显式链接 cuFFT 库支持ENABLE_CUDA_DEBUG选项开启 cuda-gdb 调试-G。按仓库根目录 README.md 的通用流程构建mkdir build cd build cmake .. make -j$(nproc)也可以在cpp/4_CUDA_Libraries/simpleCUFFT_MGPU目录内单独执行同样的 CMake 流程构建产物为可执行文件simpleCUFFT_MGPU。运行前提必须有两块“同架构”GPU这是本示例最重要的硬性约束。程序启动后会调用cudaGetDeviceCount获取节点 GPU 数量若少于 2GPU_COUNT则打印提示并以EXIT_WAIVED退出该退出码在 helper_cuda.h 中定义为 2表示跳过/放弃而非失败遍历所有 GPU用cudaGetDeviceProperties读取每块的 compute capabilitymajor/minor在i j的组合中寻找计算能力完全相同的两块 GPU记录到whichGPUs[2]找不到则同样以EXIT_WAIVED放弃运行。从源码结构看simpleCUFFT_MGPU.cu该设计保证了 FFT 数据分片在两块能力对等的设备上负载均衡。后续cufftXtSetGPUs若返回CUFFT_INVALID_DEVICE还会进一步提示需要同一板卡上的两块 GPU同样以EXIT_WAIVED跳过。算法原理FFT 加速卷积一维离散卷积定义为y[i] Σ_j x[i-j] · h[j]直接计算的时间复杂度为 O(N·M)N 为信号长度M 为滤波器长度。利用卷积定理可将卷积转为频域乘法y IFFT( FFT(x) · FFT(h) )先对信号与滤波器做正变换FFT在频域逐点相乘再做逆变换IFFT总复杂度降为 O(N log N)。本示例的规模参数定义在 simpleCUFFT_MGPU.cuconst int SIGNAL_SIZE 1018; // 信号长度 const int FILTER_KERNEL_SIZE 11; // 滤波器长度远小于信号长度 const int GPU_COUNT 2; // 使用 GPU 数量由于 FFT 要求输入长度与滤波器等长PadDatasimpleCUFFT_MGPU.cu负责把信号和滤波器都补零到统一长度int minRadius filter_kernel_size / 2; // 5 int maxRadius filter_kernel_size - minRadius; // 6 int new_size signal_size maxRadius; // 1018 6 1024关键点信号在尾部补零至new_size恰好 1024为 2 的幂最利于 FFT滤波器做循环移位式填充把后maxRadius个元素放在开头中间补零最后minRadius个元素放在末尾从而把线性卷积的因果延迟转换为频域相乘所需的循环卷积对齐由于滤波器长度11远小于信号长度1018线性卷积与循环卷积在此补零策略下结果一致。多 GPU 核心流程cufftXt 扩展 API 调用链主流程位于 simpleCUFFT_MGPU.cu共九个阶段下面逐一展开。1. 创建空计划cufftCreatecufftHandle plan_input; checkCudaErrors(cufftCreate(plan_input));cufftCreate返回一个尚未绑定具体变换规格的空 plan 句柄后续由多 GPU 专属的cufftXtSetGPUs与cufftMakePlan1d填充。这是 cuFFT XT 工作流与单 GPU 版cufftPlan1d的重要差异设备绑定与变换规格分两步完成。2. 绑定 GPU 集合cufftXtSetGPUsresult cufftXtSetGPUs(plan_input, nGPUs, whichGPUs);将之前探测到的两块同架构 GPUwhichGPUs[0]、whichGPUs[1]绑定到计划上。返回值CUFFT_INVALID_DEVICE表示设备不满足要求如不在同一板卡、不支持 P2P 等此时示例选择放弃运行。3. 制定一维变换计划cufftMakePlan1dsize_t *worksize (size_t *)malloc(sizeof(size_t) * nGPUs); checkCudaErrors(cufftMakePlan1d(plan_input, new_size, CUFFT_C2C, 1, worksize));参数依次为plan 句柄、变换点数补零后的 1024、变换类型CUFFT_C2C复数到复数、batch 数1、每块 GPU 的 workspace 大小数组。复数类型在源码中以typedef float2 Complex;定义simpleCUFFT_MGPU.cuCUFFT_C2C与cufftComplex即float2一一对应。4. 分配多 GPU 显存cufftXtMalloccudaLibXtDesc *d_signal; checkCudaErrors(cufftXtMalloc(plan_input, (cudaLibXtDesc **)d_signal, CUFFT_XT_FORMAT_INPLACE));cufftXtMalloc不返回普通指针而是返回一个cudaLibXtDesc库描述符。它记录了数据如何在多块 GPU 上分片descriptor-nGPUs为 GPU 数量descriptor-GPUs[i]为第 i 片数据所在的设备号descriptor-data[i]为第 i 片数据的设备端基址descriptor-size[i]为该片字节数。示例共分配 4 个描述符信号、滤波器的正变换输入/输出各一d_signal/d_out_signal、d_filter_kernel/d_out_filter_kernel全部采用CUFFT_XT_FORMAT_INPLACE原位格式。5. 主机到多 GPU 拷贝cufftXtMemcpycheckCudaErrors(cufftXtMemcpy(plan_input, d_signal, h_padded_signal, CUFFT_COPY_HOST_TO_DEVICE)); checkCudaErrors(cufftXtMemcpy(plan_input, d_filter_kernel, h_padded_filter_kernel, CUFFT_COPY_HOST_TO_DEVICE));与普通cudaMemcpy不同cufftXtMemcpy会依据计划绑定的 GPU 集合把主机端补零后的数据自动分发到各 GPU 的对应分片。6. 多 GPU 正变换cufftXtExecDescriptorC2CcheckCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_signal, d_signal, CUFFT_FORWARD)); checkCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_filter_kernel, d_filter_kernel, CUFFT_FORWARD));输入输出均为cudaLibXtDesc描述符方向为CUFFT_FORWARD信号与滤波器分别完成到频域的变换。7. 重排到自然顺序checkCudaErrors(cufftXtMemcpy(plan_input, d_out_signal, d_signal, CUFFT_COPY_DEVICE_TO_DEVICE)); checkCudaErrors(cufftXtMemcpy(plan_input, d_out_filter_kernel, d_filter_kernel, CUFFT_COPY_DEVICE_TO_DEVICE));FFT 执行后的数据分片布局与变换算法相关与用户自定义核函数期望的自然顺序可能不同因此先用CUFFT_COPY_DEVICE_TO_DEVICE把数据重排到d_out_*描述符中。随后示例打印描述符内容验证布局printf(Number of GPUs %d\n, d_out_signal-descriptor-nGPUs); printf(Device id %d %d\n, d_out_signal-descriptor-GPUs[0], d_out_signal-descriptor-GPUs[1]); printf(Data size on GPU %ld %ld\n, ...);8. 多 GPU 逐点相乘与归一化multiplyCoefficient频域乘法由用户自定义 CUDA 核函数完成入口为multiplyCoefficientsimpleCUFFT_MGPU.cufor (int i 0; i nGPUs; i) { device d_signal-descriptor-GPUs[i]; checkCudaErrors(cudaSetDevice(device)); ComplexPointwiseMulAndScale32, 256( (cufftComplex *)d_signal-descriptor-data[i], (cufftComplex *)d_filter_kernel-descriptor-data[i], int(d_signal-descriptor-size[i] / sizeof(cufftComplex)), val); } // 随后逐设备 cudaSetDevice cudaDeviceSynchronize getLastCudaError关键实现要点逐设备切换通过cudaSetDevice依次切到每片数据所属的 GPU 再启动核函数这是多 GPU 编程的基本模式直接从描述符取地址核函数参数直接取自descriptor-data[i]与descriptor-size[i]展示了cudaLibXtDesc的典型消费方式网格配置32, 256配合核函数内的 stride 循环simpleCUFFT_MGPU.cu覆盖整片数据归一化缩放因子val 1.0f / new_size因为 cuFFT 的正变换不做归一化需要在逆变换前补回 1/N复数运算核函数使用内联的ComplexMul与ComplexScale源码 simpleCUFFT_MGPU.cu分别实现复数乘法和复数标量乘法同步与错误检查循环结束后逐设备cudaDeviceSynchronize等待完成并用getLastCudaError检查核函数执行错误。9. 多 GPU 逆变换checkCudaErrors(cufftXtExecDescriptorC2C(plan_input, d_out_signal, d_out_signal, CUFFT_INVERSE));频域相乘后的结果做CUFFT_INVERSE逆变换回到时域。随后checkCudaErrors(cufftXtMemcpy(plan_input, h_convolved_signal, d_out_signal, CUFFT_COPY_DEVICE_TO_HOST));将分布在多块 GPU 上的结果聚合拷贝回主机端h_convolved_signal复用补零信号缓冲区得到最终卷积输出。结果验证与 CPU 参考实现比对为了保证多 GPU 实现的正确性示例提供了两套验证手段CPU 参考实现ConvolvesimpleCUFFT_MGPU.cu用最直接的双重循环按定义计算线性卷积边界处理采用半径minRadius 5、maxRadius 6即对输出点 i 累加j ∈ [-5, 5]范围内的贡献L2 范数比对bool bTestResult sdkCompareL2fe((float *)h_convolved_signal_ref, (float *)h_convolved_signal, 2 * SIGNAL_SIZE, 1e-5f);sdkCompareL2fe来自 Common 目录的辅助头文件以相对 L2 误差阈值1e-5f比较 CPU 与 GPU 两组结果长度为2 * SIGNAL_SIZE因为每个复数为两个 float返回true表示通过。程序最终以bTestResult ? EXIT_SUCCESS : EXIT_FAILURE作为进程退出码可直接用于自动化测试。资源清理与收尾示例在退出前完整释放全部资源simpleCUFFT_MGPU.cufree(whichGPUs); free(worksize); free(h_signal); free(h_filter_kernel); free(h_padded_signal); free(h_padded_filter_kernel); free(h_convolved_signal_ref); // 多 GPU 显存释放即 README 中列出的 cudaXtFree checkCudaErrors(cufftXtFree(d_signal)); checkCudaErrors(cufftXtFree(d_filter_kernel)); checkCudaErrors(cufftXtFree(d_out_signal)); checkCudaErrors(cufftXtFree(d_out_filter_kernel)); // 销毁 FFT 计划 checkCudaErrors(cufftDestroy(plan_input));注意多 GPU 场景下必须用cufftXtFree释放cufftXtMalloc分配的描述符内存不能使用普通cudaFree否则无法正确回收分布在各设备上的分片。计划则统一由cufftDestroy销毁。与系列内其他 cuFFT 示例的对比cpp/4_CUDA_Libraries目录下见 4_CUDA_Libraries/README.md包含一整套 cuFFT 卷积示例相互对照可快速理解 API 演进示例维度GPU 数关键 API 差异simpleCUFFT1D单 GPUcufftPlan1d/cufftExecC2C简单与高级 API 混合simpleCUFFT_callback1D单 GPU用 cuFFT 回调函数代替独立的逐点相乘核函数simpleCUFFT_MGPU本文1D多 GPUcufftXtSetGPUs/cufftXtMalloc/cufftXtExecDescriptorC2CsimpleCUFFT_2d_MGPU2D多 GPU同一套 cufftXt 流程变换维度变为 2D从这套对比可以清晰看到当计算规模超出单卡显存或需要多卡并行加速时只需把单 GPU 的 plan 创建与执行替换为cufftXt*系列接口其余业务逻辑补零、频域相乘、验证可以完全复用这正是 cuFFT 扩展 API 的设计价值所在。小结simpleCUFFT_MGPU虽然代码量不大却完整覆盖了多 GPU FFT 计算的六个核心环节设备探测与配对选择、数据补零、cufftXt 计划创建与 GPU 绑定、描述符式显存分配与数据分发、自定义核函数逐片处理、CPU 参考验证。阅读源码 simpleCUFFT_MGPU.cu 时建议重点关注cudaLibXtDesc描述符如何贯穿cufftXtMalloc、cufftXtMemcpy、cufftXtExecDescriptorC2C与自定义核函数——理解了描述符的分片语义也就掌握了 cuFFT 多 GPU 编程的钥匙。【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表