CUDA Tile C++ 实现 Rotary Position Embedding(RoPE)前向传播:基于 cuda-samples tileRope 示例的源码级解析
CUDA Tile C 实现 Rotary Position EmbeddingRoPE前向传播基于 cuda-samples tileRope 示例的源码级解析【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples导读本文围绕 cuda-samples 仓库 cpp/9_CUDA_Tile/tileRope 示例讲解如何用 CUDA Tile C 编写 Transformer 注意力层中 Rotary Position EmbeddingRoPE的前向传播内核。该示例采用 split-halfGPT-NeoX 风格约定通过cuda::tiles::partition_view将 Q/K 张量切分为 (heads, half_rope_dim) 二维 tile并以一个 block 并行处理单个 token 的全部 head的布局完成就地in-place旋转。读完本文你将掌握 RoPE 的数学原理、Tile C 的分块加载/广播/写回模式以及如何借助 CPU 参考实现验证内核正确性。RoPE 原理与 split-half 约定RoPERotary Position Embedding将位置信息注入注意力层的 Q 与 K 投影它不改变特征向量的模长而是按位置相关的角度在 head 维度上旋转成对的特征使点积自然携带相对位置编码。tileRope 示例见 tileRope/README.md采用的 split-half 约定如下对位置s处的 token配对(q[i], q[i D/2])以角度theta s * 10000^(-2i / D)旋转其中D为 head 维度i为频率下标0 i D/2。旋转公式为q[i] q[i]*cos(theta) - q[i D/2]*sin(theta) q[i D/2] q[i]*sin(theta) q[i D/2]*cos(theta)该约定在 tileRope.cu 的注释与 9_CUDA_Tile/README.md 中均有明确说明。与将特征对(q[2i], q[2i1])相邻配对的原始 RoPE 不同split-half 风格将前半个 head 维度与后半个 head 维度配对因此旋转 pair 天然对应两个半区 tile非常适合用二维分块表达tile 0 覆盖[0 : BLOCK_HD)前半tile 1 覆盖[BLOCK_HD : 2*BLOCK_HD)后半见 tileRope.cu。编译期形状参数一次编译、shape 全折叠示例将问题形状全部固化为编译期常量tileRope.cu常量值含义BATCH1batch 大小Q_HEADS/K_HEADS8 / 8Q、K 的 head 数SEQ_LEN64序列长度token 数HEAD_DIM64每 head 的特征维度DHALF_ROPE_DIM32D / 2旋转频率下标范围BLOCK_QH/BLOCK_KH8 / 8tile 沿 head 维的大小此处等于 head 总数BLOCK_HD32tile 沿 half_rope_dim 维的大小COS_BS1cos/sin 表的 batch 维度1 表示所有 batch 共享同一张表由此派生的张量字节规模为Q_SIZE BATCH * Q_HEADS * SEQ_LEN * HEAD_DIM、K_SIZE同理COS_SIZE COS_BS * SEQ_LEN * HALF_ROPE_DIM。这些常量随后作为rope内核的模板参数传入tileRope.cu让 tile 编译器可以把循环步长、partition_view的 extent 等在编译期全部折叠消除运行时索引开销——这与同分类下 tileLayerNorm 通过编译期模板参数折叠(1/D)倒数与 eps 广播的设计思路一致。数据布局与 SIMT 初始化内核cos/sin 表的布局cos/sin 表按(COS_BS, SEQ_LEN, HALF_ROPE_DIM)组织tileRope.cu即每个 (batch, position, frequency-index) 三元组一条目。由于COS_BS 1所有 batch 共享同一张位置编码表。initializeInputs SIMT 内核Q、K 及 cos/sin 表的初始化由一个普通 SIMT 内核完成tileRope.cuQ 的第d个特征填充为float(d % 11) / 10.0f - 0.5fK 的第d个特征填充为float(d % 13) / 10.0f - 0.5f以__half半精度存储取值控制在[-0.5, 1.0)区间保证后续旋转不溢出cos/sin 条目由theta s * powf(10000.0f, -2.0f * i / HEAD_DIM)计算后取cosf/sinf得到与 README 中的公式逐项对应。初始化时每个线程通过tid blockIdx.x * blockDim.x threadIdx.x展开为一个扁平索引并以INIT_N max(Q_SIZE, K_SIZE, COS_SIZE)为界统一覆盖三块缓冲区主函数中以 256 线程/block 启动tileRope.cu。Tile 内核partition_view 分块与就地旋转内核签名与启动rope是一个__tile_global__内核tileRope.cu对 Q、K、cos、sin 均声明__restrict__指针并调用ct::assume_aligned16提示编译器按 16 字节对齐优化与 tileVectorAdd 中的对齐优化手法一致。网格大小为BATCH * SEQ_LEN即每个 block 负责一个 (batch, position) token一个 block 内并行处理全部 headrope__half, BATCH, Q_HEADS, K_HEADS, BLOCK_QH, BLOCK_KH, BLOCK_HD, HALF_ROPE_DIM, HEAD_DIM, COS_BS, SEQ_LEN BATCH * SEQ_LEN(d_q, d_k, d_cos, d_sin);从 block id 映射到 token 坐标内核入口通过ct::bid().x取得 block idpid再解析出 batch 与行号int batch_idx pid / SEQ_LEN_; int row_idx pid % SEQ_LEN_; int cos_batch_idx (COS_BS_ 1) ? 0 : batch_idx;其中cos_batch_idx的处理体现了表共享语义当COS_BS 1时所有 batch 统一取第 0 张表。cos/sin 行的加载与广播cos/sin 表通过ct::partition_view按shape1, 1, BLOCK_HD分块以(cos_batch_idx, row_idx, 0)三元索引加载对应 token 行的半个 head 维度随后reshapeshape1, BLOCK_HD成一行 tiletileRope.cu。由于该行需要作用于所有 head示例用ct::broadcastshapeBLOCK_QH_, BLOCK_HD_(cos_row)将 cos/sin 行沿 head 维广播tileRope.cu。Q/K 两半 tile 的旋转与写回Q 被建模为extents{BATCH_, Q_HEADS_, SEQ_LEN_, HEAD_DIM_}的四维张量partition_view以shape1, BLOCK_QH_, 1, BLOCK_HD_分块沿 head_dim 轴的第 0、1 个 tile 分别对应前半[0 : BLOCK_HD)与后半[BLOCK_HD : 2*BLOCK_HD)tileRope.cu。两个 tile 加载后 reshape 为(BLOCK_QH, BLOCK_HD)的二维 tile旋转计算以极简洁的逐元素表达式完成new_q_tile_1 q_tile_1 * cos_bcast_q - q_tile_2 * sin_bcast_q; new_q_tile_2 q_tile_2 * cos_bcast_q q_tile_1 * sin_bcast_q;这正是 README 中两条旋转公式的向量化tile 化形式。结果经reshapeshape1, BLOCK_QH_, 1, BLOCK_HD_还原为四维分块形状后通过pQ.store就地写回两个半区tileRope.cu。K 的处理与 Q 完全对称tileRope.cuhead 维 tile 大小使用BLOCK_KH_。值得注意由于分块形状中 token 维与 head 维均为 1 个 tile位于rope_dim之后的元素HEAD_DIM 2 * BLOCK_HD时保持原值不变本示例中HEAD_DIM 2 * BLOCK_HD因此全部维度均参与旋转。CPU 参考实现与正确性验证示例内置了与 split-half 约定完全对齐的 CPU 参考验证函数verifytileRope.cu对每个 (batch, head, seq, i) 组合取输入半区对(h_in[basei], h_in[baseiHALF_ROPE_DIM])与表项(h_cos[ci], h_sin[ci])按公式计算期望值exp1/exp2与内核输出比较容差设为1e-1f半精度运算下的合理误差界任一元素超差即打印Mismatch in Q/K at (b..., h..., s..., i...)的详细期望/实际值并返回失败Q、K 各自独立验证全部通过后主函数打印预期输出。验证流程的完整调用链为tileRope.cuSIMT 初始化 →cudaMemcpy将输入快照到主机因为内核是就地写回必须先保存原始值→ 启动 tile 内核 →cudaDeviceSynchronize→ 拷回结果 → 逐元素比对。README 中声明的预期输出为Success! RoPE matches expected results.构建与运行前提条件依据 tileRope/README.md本示例需要CUDA Toolkit 13.3 或更高版本CUDA Driver 580 或更高版本支持 C20 的主机编译器示例 CMake 中通过target_compile_features(tileRope PRIVATE cxx_std_20 cuda_std_20)强制要求见 CMakeLists.txt。关键编译配置tileRope/CMakeLists.txt 中有两个值得注意的点--enable-tile编译开关set(CMAKE_CUDA_FLAGS ${CMAKE_CUDA_FLAGS} --enable-tile)这是 CUDA Tile C 内核__tile_global__能够被 nvcc 正确编译的前提架构列表CMAKE_CUDA_ARCHITECTURES覆盖 80/86/87/89/90/100/110/120即从 Ampere 到 Blackwell 的现代架构。构建步骤tileRope是cpp/9_CUDA_Tile分类下的一个子示例9_CUDA_Tile/CMakeLists.txt 通过add_subdirectory(tileRope)注册。可进入该示例目录独立构建也可随整个仓库一起构建Linux 下标准流程为mkdir build cd build cmake .. make -j$(nproc)完整构建指引见仓库根 README.md。构建完成后运行./tileRope若内核与 CPU 参考一致将输出Success! RoPE matches expected results.小结tileRope 是理解如何用 CUDA Tile C 表达张量分块运算的极佳范例它将 RoPE 这一典型 Attention 前向算子拆解为SIMT 初始化数据准备→ tile 内核分块加载 广播 就地旋转写回→ CPU 参考验证三段式流程并通过编译期 shape 参数实现最大程度的代码折叠。其partition_view沿 head_dim 轴取两个半区 tile、broadcast跨 head 复用 cos/sin 行的写法可直接迁移到 bf16/fp16 的 GQA/MHA 等真实推理场景中是 9_CUDA_Tile 分类中兼具教学价值与工程参考意义的实现。【免费下载链接】cuda-samplesSamples for CUDA Developers which demonstrates features in CUDA Toolkit项目地址: https://gitcode.com/GitHub_Trending/cu/cuda-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →