CUDA Lane ID硬件读取与threadIdx.x&31计算一致性及断言有效性验证
问题解答
两种Lane ID获取方式是否在1D网格场景下完全一致?
答案是不一定完全一致,仅在特定条件下两者结果相同:
- 硬件层面的Lane ID(通过
cub::LaneId()、汇编读%laneid得到的结果)是线程所属warp内的硬件编号,固定为0~31,和软件层面的线程索引组织逻辑无关。 threadIdx.x & 31是软件计算值,要和硬件Lane ID完全相等,需要同时满足两个前提:- 不仅网格是1D,线程块(block)也必须是1D组织(即
blockDim.y = 1、blockDim.z = 1) - 没有使用CUDA的动态warp分区等特殊调度特性(普通开发场景基本不会涉及)
- 不仅网格是1D,线程块(block)也必须是1D组织(即
- 如果线程块是2D/3D组织,即使网格是1D,线程在warp内的排序是按线性线程索引
threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * blockDim.x * blockDim.y划分的,此时threadIdx.x & 31和硬件Lane ID没有对应关系,结果必然不一致。
给出的代码断言是否永远不会触发失败?
答案是两个断言都可能触发失败,原因如下:
对应代码片段:
int nWarps = /*...*/; bool condition = /*...*/; if(threadIdx.x < nWarps) { assert(__activemask() == ((1u<<nWarps)-1)); uint32_t res = __ballot_sync(__activemask(), condition); assert(bool(res & (1<<threadIdx.x)) == condition); }
第一个断言失败场景
- 当
nWarps > 32时,1u << nWarps会出现32位无符号数溢出,(1u << nWarps) -1的结果完全不符合预期,断言直接失败。 - 满足
threadIdx.x < nWarps的线程可能分布在多个warp中,不同warp的__activemask()互相独立,不可能等于低nWarps位全1的掩码。 - 即使所有满足条件的线程在同一个warp内,如果之前存在分支 divergence,部分满足
threadIdx.x <nWarps的线程已经被置为非活跃状态,__activemask()的结果也会和预期不符。 - 如果
threadIdx.x和硬件Lane ID不相等(比如线程块是2D/3D组织),__activemask()的置位位和threadIdx.x没有对应关系,断言必然失败。
第二个断言失败场景
__ballot_sync()的返回值是按硬件Lane ID置位的,第N位对应Lane ID为N的线程的投票结果,而代码中用1 << threadIdx.x取对应位,只要threadIdx.x不等于当前线程的Lane ID,取到的位就不是当前线程的投票结果,断言会失败。- 当
threadIdx.x >=32时,1 << threadIdx.x会出现整数溢出,属于未定义行为,且__ballot_sync()返回值是32位无符号数,不存在超过31的位,断言必然失败。
内容的提问来源于stack exchange,提问作者Serge Rogatch
相关产品推荐
相关产品推荐

