简介#
Warp是SIMT线程执行和调度的基本单位。一个线程块内的线程按照线性线程索引划分为多个Warp,当前一个Warp包含32个线程。同一Warp内的线程执行相同的指令流,每个线程拥有独立的寄存器和Lane ID,可以处理不同的数据地址和分支路径。
Warp函数用于同一Warp内的轻量级线程协作,提供Lane信息查询、条件投票、寄存器数据交换和Warp内归约等能力。与通过Unified Buffer(UB)中转数据相比,Warp Shuffle函数可以直接交换Lane持有的标量值,适合低开销的小范围协作。
相关基本概念如下表所示:
概念 |
说明 |
|---|---|
Warp |
SIMT执行中的线程分组,当前一个Warp包含32个线程。 |
Lane |
Warp内单个线程对应的执行位置。 |
Lane ID |
线程在当前Warp内的编号,取值范围为[0, 31]。 |
活跃线程 |
在当前指令位置实际参与执行的线程,也称为活跃Lane。活跃状态与指令位置有关,不是线程的固定属性。 |
分支发散 |
同一Warp内的线程进入不同分支路径。硬件依次执行各条路径,并暂时屏蔽不属于当前路径的Lane。 |
逻辑子组 |
Shuffle函数通过width将一个Warp划分得到的独立交换分组。 |
活跃线程是指在当前指令位置实际执行指令的线程。活跃状态会随着程序执行位置变化:某个Lane在一条分支路径中可能暂时处于非活跃状态,进入后续公共路径后又会重新处于活跃状态。因此,“非活跃”通常不表示线程已经结束,只表示该Lane不执行当前路径上的指令。
当同一Warp内的Lane根据各自的数据进入不同分支时,会发生分支发散。硬件先执行其中一条路径,并屏蔽不属于该路径的Lane;再执行其他路径;各条路径结束后,Lane在公共路径上重新汇合。下面以一个包含32个Lane的Warp为例,前后各16个Lane进入不同分支:
lane = pl.simt.lane_id()
if lane < 16:
lower_mask = pl.simt.warp_active_mask()
else:
upper_mask = pl.simt.warp_active_mask()
merged_mask = pl.simt.warp_active_mask()
各调用位置观察到的活跃Lane如下:
调用位置 |
正在执行的Lane |
32位活跃掩码(bit 31到bit 0) |
|---|---|---|
lower_mask |
Lane 0~15 |
00000000 00000000 11111111 11111111 |
upper_mask |
Lane 16~31 |
11111111 11111111 00000000 00000000 |
merged_mask |
Lane 0~31 |
11111111 11111111 11111111 11111111 |
这里的lower_mask和upper_mask虽然位于同一个Warp中,但处于不同分支路径,因此得到不同的活跃Lane集合。分支结束后,32个Lane在merged_mask处重新汇合。