
cuda-samples 中 cdpQuadtree 详解基于 CUDA Dynamic Parallelism 与 Cooperative Groups 的 GPU 四叉树构建【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples导读cdpQuadtree是 CUDA Samples 仓库 cpp/3_CUDA_Features/cdpQuadtree 目录下的一个示例程序演示如何利用CUDA Dynamic ParallelismCDP动态并行在 GPU 上直接、递归地构建一棵二维四叉树Quad Tree。示例在 CUDA 运行时 API 之上还引入了Cooperative Groups编程模型借助线程块级同步与 warp 级内建函数__ballot、__popc、shfl完成点的四象限归并计数与紧凑搬移。阅读完本文你将掌握CDP 的启动方式与适用前提、四叉树在 GPU 上递归建树的完整五步流程、双缓冲点集交换的缘由以及该示例从编译到运行验证的完整链路。示例概述与关键概念根据 cdpQuadtree/README.md 的说明本示例的核心主题是使用 CUDA Dynamic Parallelism 实现 Quad Trees四叉树。该示例要求设备的compute capabilitySM 架构不低于 3.5因为 CDPGPU 内核从设备线程中再次启动内核能力正是从 SM 3.5 开始引入的。README 列出的两大关键概念为Cooperative Groups用于在线程块内部实现显式、可移植的同步cg::sync以及对 warp 做 tiled partition从而以 tile 为单位使用 ballot/shuffle 内建函数。CUDA Dynamic Parallelism允许 GPU 上的 kernel 递归地启动新的 kernel本示例中每个四叉树节点由一个 block 负责节点分裂时该 block 直接启动 4 个新 block 处理 4 个子节点。在仓库根目录 README.md 的 CUDA Features 一节中也明确给出 CDP 的定义CDP 允许从运行在 GPU 上的线程中启动 kernel且仅在 SM 架构为 3.5 或以上的 GPU 上可用。这是理解本示例硬件前提的权威依据。支持平台与依赖README 记录了示例的完整支持矩阵与依赖支持的 SM 架构3.5 及以上CDP 能力要求。支持的操作系统Linux、Windows。支持的 CPU 架构x86_64、armv7l。依赖需要 CUDA Toolkit 中提供的 CDP 支持对应仓库根 README.md 中 CUDA Dynamic Parallellism 一节并在构建前安装与平台匹配的 CUDA Toolkit。代码层面main函数在运行前会做一次硬性的能力检查见 cdpQuadtree.cuint cdpCapable (deviceProps.major 3 deviceProps.minor 5) || deviceProps.major 4; if (!cdpCapable) { std::cerr cdpQuadTree requires SM 3.5 or higher to use CUDA Dynamic Parallelism. Exiting...\n std::endl; exit(EXIT_WAIVED); }即SM 3.5 及以上才继续执行否则以EXIT_WAIVED跳过退出。该能力判断逻辑与 README 中 compute capability 3.5 or higher 的要求完全对应。示例涉及的 CUDA Runtime APIREADME 列出了本示例用到的 CUDA Runtime APIAPI用途结合源码cudaMalloc为Points结构与四叉树节点数组分配设备内存cdpQuadtree.cu、cdpQuadtree.cucudaMemcpy将主机端的Points指针结构与根节点拷贝到设备端cdpQuadtree.cu、cdpQuadtree.cu、cdpQuadtree.cucudaFree释放节点数组与点集结构内存cdpQuadtree.cucudaGetDeviceProperties查询设备 SM 主次版本号与 warpSize用于 CDP 能力判断cdpQuadtree.cucudaGetLastError启动根 kernel 后检查是否存在异步启动错误cdpQuadtree.cucudaDeviceSetLimitREADME 中列为涉及 API用于调整 CDP 设备运行时相关限制如 pending launch 队列但本示例源码中并未显式调用实际使用可参考同目录下 cdpAdvancedQuicksort/cdpAdvancedQuicksort.cu 中cudaDeviceSetLimit(cudaLimitDevRuntimePendingLaunchCount, 4096)的用法源码结构与核心数据结构示例仅由单个源文件 cdpQuadtree.cu 组成外加 CMakeLists.txt 构建脚本代码结构清晰Points以结构体数组SoA方式存储的二维点集m_x/m_y两个float*分别指向 x、y 坐标数组提供get_point/set_point读写接口并用__host__ __device__双端注解保证主机与设备均可访问。Bounding_box二维轴对齐包围盒保存m_p_min/m_p_max两个极值点提供contains点是否落入盒内、compute_center计算盒中心等方法。Quadtree_node四叉树节点记录节点 ID、所属包围盒以及该节点在点集缓冲中的范围[m_begin, m_end)从而通过num_points()快速得到节点内的点数。Parameters算法参数聚合体包含point_selector当前读写的点缓冲索引、num_nodes_at_this_level当前层节点数第 k 层为 4^k、depth递归深度、max_depth最大深度、min_points_per_node单节点最小点数。其拷贝构造函数会为下一次递归迭代更新point_selector、num_nodes_at_this_level×4与depth1。这些数据结构的定义分别位于 cdpQuadtree.cuPoints、cdpQuadtree.cuBounding_box、cdpQuadtree.cuQuadtree_node、cdpQuadtree.cuParameters。核心算法CDP 递归建树的五步流程build_quadtree_kernelNUM_THREADS_PER_BLOCKcdpQuadtree.cu是整棵四叉树构建的核心。算法策略为主机CPU仅启动 1 个 block每个四叉树节点对应一个 block节点需要分裂时该 block 中的最后一个线程再启动 4 个新 block每个子节点一个形成 GPU 侧的递归。源码注释cdpQuadtree.cu给出了清晰的五步流程步骤 1检查点数与深度决定是否终止递归if (params.depth params.max_depth || num_points params.min_points_per_node) { if (params.point_selector 1) { // 把 points[1] 中的点拷回 points[0] for (it threadIdx.x; it end; it NUM_THREADS_PER_BLOCK) if (it end) points[0].set_point(it, points[1].get_point(it)); } return; }当深度达到max_depth或节点点数不超过min_points_per_node时该 block 的线程直接退出。退出前需要一次缓冲交换算法使用两块点缓冲轮流读写乒乓而设计目标是在算法结束时所有点都位于points[0]因此若当前读缓冲是points[1]需把点搬回points[0]。步骤 2统计每个孩子象限中的点数对每个需继续分裂的节点先计算包围盒中心center然后按中心把点划分为四个几何桶子节点象限左上、右上、左下、右下。点集被均分为多个区段每个 warp32 线程负责一个区段使用__ballot与__popc内建函数完成计数。计数部分cdpQuadtree.cu通过 Cooperative Groups 的tile3232 线程的 tile实现int num_pts __popc(tile32.ballot(is_active p.x center.x p.y center.y)); // 左上 warp_cnts[0] tile32.shfl(num_pts, 0);ballot将 32 个线程的谓词打包成一个 32 位掩码popc统计置位个数即该 warp 命中该象限的点数随后shfl把结果广播给 warp 内所有线程累加。步骤 3对 warp 结果做块级扫描Scan/Reduce各个 warp 独立工作彼此只知道自己区段的计数因此需要块级扫描来得到全局偏移。代码让前 4 个 warp 分别对 4 个象限的 warp 计数做 inclusive scancdpQuadtree.cu再由 warp 0 累加各象限总和得到全局偏移最后转为 exclusive scan 并叠加node.points_begin()作为写回目标偏移cdpQuadtree.cu。源码注释也提醒该实现不像快速基数排序那样高度优化但遵循同样的 scan 思想。步骤 4搬移点获得每个象限的目标偏移后逐点计算其在输出缓冲中的目的位置并写入。核心技巧是基于__ballotlane_mask_lt的紧凑搬移cdpQuadtree.cuint vote tile32.ballot(pred); // 本 warp 命中象限的掩码 int dest warp_cnts[0] __popc(vote lane_mask_lt); // 该点在象限内的序号 if (pred) out_points.set_point(dest, p); warp_cnts[0] tile32.shfl(__popc(vote), 0); // 累加本 warp 的命中数lane_mask_lt (1 lane_id) - 1得到lane id 小于当前线程的掩码等价于 PTX 的lanemask_lt见源码注释从而高效计算 warp 内位于当前点之前的命中个数。四个象限上/下、左/右分别处理一遍。步骤 5启动新的 blockCDP 递归由块内最后一个线程threadIdx.x NUM_THREADS_PER_BLOCK - 1为 4 个子节点设置 ID、子包围盒按中心四等分与点范围然后以网格维度 4 启动子 kernelcdpQuadtree.cubuild_quadtree_kernelNUM_THREADS_PER_BLOCK 4, NUM_THREADS_PER_BLOCK, 4 * NUM_WARPS_PER_BLOCK * sizeof(int)( children[child_offset], points, Parameters(params, true));注意这里同时用到了Cooperative Groups 的块级同步cg::sync(cta)而非__syncthreads保证计数、扫描、搬移各阶段之间的可见性与同步共享内存中动态分配了4 * NUM_WARPS_PER_BLOCK个int用于存放各象限各 warp 的计数。算法参数与运行配置主机函数cdpQuadtreecdpQuadtree.cu中定义了核心运行参数参数默认值含义num_points1024随机生成的点数在单位正方形内均匀分布max_depth8四叉树最大深度超过即停止分裂min_points_per_node16节点内点数下限少于等于该值停止分裂NUM_THREADS_PER_BLOCK128每个 block 的线程数源码注释明确要求不要少于 128需保证至少 4 个 warp 用于扫描阶段由此可推导节点容量上界max_nodes Σ_{i0}^{7} 4^i即按每层 4 的幂累加求得主机端在 cdpQuadtree.cu 中计算后一次性cudaMalloc分配。点的生成使用 Thrust 库的thrust::generate配合自定义Random_generatorcdpQuadtree.cu基于thrust::default_random_engine与uniform_real_distributionfloat生成 [0,1) 均匀分布的二维点并通过哈希种子与cuda::std::tuple返回坐标对。结果验证算法执行完后主机端将节点与点拷贝回 CPU调用check_quadtreecdpQuadtree.cu递归校验两类不变式点数守恒非叶子节点的 4 个子节点点数之和必须等于父节点点数cdpQuadtree.cu空间正确性叶子节点中的每个点都必须落在该节点包围盒内bbox.contains(p)cdpQuadtree.cu。校验通过后程序输出Results: OK并以EXIT_SUCCESS退出失败则输出FAILED并返回EXIT_FAILURE。构建与运行目录下的 CMakeLists.txt 是该示例的 CMake 构建脚本关键点如下需要find_package(CUDAToolkit REQUIRED)且project(cdpQuadtree LANGUAGES C CXX CUDA)声明了 CUDA 语言。非 aarch64 平台默认的CMAKE_CUDA_ARCHITECTURES为75 80 86 89 90 100 110 120aarch64 Tegra 工具链CUDA Toolkit 13.0 起则为87 110兼容性覆盖较广。通过target_compile_features要求cxx_std_17与cuda_std_17并通过target_compile_options传入--extended-lambdaThrust 生成调用需要。设置CUDA_SEPARABLE_COMPILATION ON这是 CDP 设备端启动dynamic parallelism的常见构建要求。通过include(../../../cmake/InstallSamples.cmake)接入仓库统一的安装配置。你可以按 CUDA Samples 仓库的标准方式构建整个3_CUDA_Features子目录该目录在 cpp/3_CUDA_Features/CMakeLists.txt 中以add_subdirectory(cdpQuadtree)纳入或在支持 CUDA 的机器上单独对该目录执行 CMake 配置与构建。运行cdpQuadtree可执行文件后程序会输出所用 GPU 的型号与 SM 版本随后打印Launching CDP kernel to build the quadtree与验证结果Results: OK若 GPU 不支持 CDPSM 3.5则打印提示并退出。小结cdpQuadtree是一份将CUDA Dynamic Parallelism与Cooperative Groups结合使用的教科书式示例前者让 GPU 端 kernel 递归启动子 block 以自然表达树的深度分裂后者为块内同步与 warp 级 ballot/shuffle 提供了现代、可移植的编程接口。它完整覆盖了四叉树 GPU 构建中的计数、块级扫描、基于__ballot的紧凑搬移、双缓冲乒乓交换与结果自校验等关键技法可作为研究动态并行、空间索引结构并行化的理想起点。【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考