当前位置: 首页 > news >正文

__shfl_down_sync()用法理解

这是理解__shfl_down_sync()最好的方法。

假设 Warp 中 32 个线程:

Lane0 = lane(0) Lane1 = lane(1) Lane2 = lane(2) ... Lane31 = lane(31)

这里的lane(i)表示线程 i 最初持有的数据。

例如:

val=lane(threadIdx.x);

那么:

val: 0 1 2 3 4 5 6 7 ... 31

第1轮:offset=16

执行:

val+=__shfl_down_sync(mask,val,16);

含义:

Lane0 += Lane16 Lane1 += Lane17 Lane2 += Lane18 ... Lane15 += Lane31

结果:

Lane0 = lane(0)+lane(16) Lane1 = lane(1)+lane(17) Lane2 = lane(2)+lane(18) ... Lane15 = lane(15)+lane(31) Lane16~31 不重要

此时前16个线程已经各自保存了2个元素的和。


第2轮:offset=8

执行:

val+=__shfl_down_sync(mask,val,8);

例如:

Lane0 += Lane8

而 Lane8 当前已经不是原始值了:

Lane8 = lane(8)+lane(24)

因此:

Lane0 = (lane(0)+lane(16)) + (lane(8)+lane(24))

即:

Lane0 = lane(0)+lane(8)+lane(16)+lane(24)

继续展开:

Lane1 = lane(1)+lane(9)+lane(17)+lane(25) Lane2 = lane(2)+lane(10)+lane(18)+lane(26) ... Lane7 = lane(7)+lane(15)+lane(23)+lane(31)

此时:

Lane0~7 每个线程拥有4个元素之和

第3轮:offset=4

执行:

val+=__shfl_down_sync(mask,val,4);

Lane0:

Lane0 += Lane4

而:

Lane4 = lane(4)+lane(12)+lane(20)+lane(28)

因此:

Lane0 = lane(0)+lane(8)+lane(16)+lane(24) + lane(4)+lane(12)+lane(20)+lane(28)

整理:

Lane0 = lane(0)+lane(4)+lane(8)+lane(12) +lane(16)+lane(20)+lane(24)+lane(28)

已经包含:

8个元素

同理:

Lane1 = 1,5,9,13,17,21,25,29 Lane2 = 2,6,10,14,18,22,26,30 Lane3 = 3,7,11,15,19,23,27,31

第4轮:offset=2

执行:

val+=__shfl_down_sync(mask,val,2);

Lane0:

Lane0 += Lane2

Lane2 当前拥有:

2,6,10,14,18,22,26,30

因此:

Lane0 = 0,4,8,12,16,20,24,28 + 2,6,10,14,18,22,26,30

得到:

Lane0 = 0,2,4,6,8,10,12,14 16,18,20,22,24,26,28,30

共:

16个元素

第5轮:offset=1

执行:

val+=__shfl_down_sync(mask,val,1);

Lane0:

Lane0 += Lane1

Lane1 当前拥有:

1,3,5,7,9,11,13,15 17,19,21,23,25,27,29,31

于是:

Lane0 = 0,2,4,6,8,10,12,14 16,18,20,22,24,26,28,30 + 1,3,5,7,9,11,13,15 17,19,21,23,25,27,29,31

最终:

Lane0 = lane(0)+lane(1)+lane(2)+...+lane(31)

即 Warp 所有线程数据之和。


用树来理解

第一轮:

0←16 1←17 2←18 ... 15←31

第二轮:

0←8 1←9 ... 7←15

第三轮:

0←4 1←5 2←6 3←7

第四轮:

0←2 1←3

第五轮:

0←1

画成树:

Lane0 / \ Lane0 Lane16 / \ Lane0 Lane8 / \ Lane0 Lane4 / \ Lane0 Lane2 / \ 0 1

实际上是:

32 ↓ 16组 ↓ 8组 ↓ 4组 ↓ 2组 ↓ 1组

每轮把参与求和的数据量扩大一倍:

offset=16 → 每线程2个数 offset=8 → 每线程4个数 offset=4 → 每线程8个数 offset=2 → 每线程16个数 offset=1 → 每线程32个数

所以你可以记住一个规律:

第 k 轮结束后 Lane0 保存了 2^k 个 lane 的部分和

最后一轮结束:

Lane0 保存 32 个 lane 的总和

这就是__shfl_down_sync()Warp Reduction 的本质。

为什么不用个函数直接把所有的lane值相加???

这是个非常好的问题。

你可以理解为:

GPU 硬件只提供了“从另一个 lane 读取寄存器值”的能力(Shuffle),并没有提供“把整个 Warp 的所有寄存器自动求和”的通用指令。


CPU 的情况

例如 CPU 有:

c=a+b;

对应一条加法指令:

ADD

甚至还有:

POPCNT

直接统计二进制中 1 的个数。

因为这些操作非常常见,CPU 专门做了硬件支持。


GPU Warp 内部

Warp 有:

Lane0 Lane1 Lane2 ... Lane31

每个 Lane 都有自己的寄存器:

R0 R1 R2 ... R31

硬件提供的是一个 Crossbar 网络:

+-----------+ Lane0 --->| | Lane1 --->| Crossbar | Lane2 --->| | ... +-----------+

因此支持:

__shfl_down_sync(...)

本质:

把 Lane17 的寄存器值送给 Lane1

或者:

把 Lane5 的寄存器值送给 Lane2

这种任意线程间的数据交换。


为什么不直接做 Warp Sum?

假设增加一个硬件指令:

warp_sum(val);

那么硬件需要支持:

32个输入 ↓ 求和树 ↓ 输出结果

类似:

+ / \ + + / \ / \ ...

对于:

32输入 64输入(AMD Wavefront) 128输入(未来GPU)

都要专门设计电路。


而 Shuffle 更通用:

__shfl_down_sync()__shfl_up_sync()__shfl_sync()__shfl_xor_sync()

既可以实现:

  • Sum
  • Max
  • Min
  • Scan
  • Prefix Sum
  • FFT
  • Matrix 运算

硬件只需要提供:

Warp Crossbar

即可。


实际上新 GPU 已经有了

近几年 NVIDIA 已经发现:

Warp Reduction

太常用了。

因此增加了专门指令:

__reduce_add_sync()

例如:

unsignedmask=0xffffffff;intsum=__reduce_add_sync(mask,val);

效果:

Warp内所有线程的 val 求和

等价于:

for(offset=16;offset>0;offset>>=1){val+=__shfl_down_sync(mask,val,offset);}

但这是较新的 GPU 架构才支持。


CUDA Cooperative Groups

还有更高级的写法:

#include<cooperative_groups.h>namespacecg=cooperative_groups;autowarp=cg::tiled_partition<32>(cg::this_thread_block());intsum=cg::reduce(warp,val,cg::plus<int>());

看起来就是:

warp.reduce()

实际上底层还是:

shuffle ↓ reduction tree

AMD HIP 为什么仍然常见 __shfl_down

因为:

NVIDIA Warp = 32 AMD Wavefront = 64

不同架构支持情况不同。

为了:

兼容性 性能 可移植性

很多 HPC 代码仍然写:

for(intoffset=warpSize/2;offset>0;offset>>=1){val+=__shfl_down(val,offset);}

这样:

  • CUDA 能跑
  • HIP 能跑
  • 不依赖新硬件

更深层原因

GPU 的设计哲学是:

提供最基础的数据交换能力 + 让程序员组合出各种算法

而不是:

提供大量固定功能指令

所以硬件提供:

Shuffle

你自己实现:

Sum Max Min Scan Sort FFT

这样硬件更简单、用途更广。

因此历史上没有一个通用的:

warp_sum()

函数。

不过在新 CUDA 中实际上已经有:

__reduce_add_sync()

只是它出现得比较晚,而__shfl_down_sync()已经成为 GPU 编程中最经典、最通用的 Warp Reduction 实现方式。

其他Lane的值是什么

这是很多人第一次看 Warp Reduction 时容易疑惑的地方。

关键点:

只有 Lane0 的结果是我们真正关心的。

Lane1、Lane2、Lane3… 在归约过程中也会不断计算,但它们保存的是各种“部分和”,最后并不是完整结果。


假设初始:

Lane0 = lane(0) Lane1 = lane(1) Lane2 = lane(2) ... Lane31 = lane(31)

执行:

for(intoffset=16;offset>0;offset>>=1){val+=__shfl_down_sync(0xffffffff,val,offset);}

offset = 16 后

Lane0 = 0+16 Lane1 = 1+17 Lane2 = 2+18 ... Lane15 = 15+31

offset = 8 后

Lane0 = 0+8+16+24 Lane1 = 1+9+17+25 Lane2 = 2+10+18+26 ... Lane7 = 7+15+23+31

offset = 4 后

Lane0 = 0+4+8+12+16+20+24+28 Lane1 = 1+5+9+13+17+21+25+29 Lane2 = 2+6+10+14+18+22+26+30 Lane3 = 3+7+11+15+19+23+27+31

每个 Lane 包含 8 个元素。


offset = 2 后

Lane0

Lane0 += Lane2

得到:

Lane0 = 0+2+4+6+8+10+12+14 +16+18+20+22+24+26+28+30

16个元素。


Lane1

Lane1 += Lane3

得到:

Lane1 = 1+3+5+7+9+11+13+15 +17+19+21+23+25+27+29+31

也是16个元素。


最后一轮 offset = 1

Lane0

Lane0 += Lane1

得到:

Lane0 = 0+1+2+3+... +31

完整32个元素。


Lane1

Lane1 += Lane2

注意:

Lane2 在上一轮已经变成:

Lane2 = 2+4+6+8+10+12+14 +16+18+20+22+24+26+28+30

(实际上包含从2开始的一组16个元素)

因此:

Lane1 = (1+3+5+...+31) + (2+4+6+...+30)

结果:

Lane1 = 1+2+3+4+... +31

少了 lane(0)


Lane2

同理:

Lane2 += Lane3

最后:

Lane2 = 2+3+4+... +31

少了:

0 1

Lane3

最后:

Lane3 = 3+4+5+... +31

少了:

0 1 2

规律

最终循环结束后:

Lane0 = sum(0~31) Lane1 = sum(1~31) Lane2 = sum(2~31) Lane3 = sum(3~31) ... Lane30 = lane(30)+lane(31) Lane31 = lane(31)

可以画成:

Lane0 = 0+1+2+3+...+31 Lane1 = 1+2+3+...+31 Lane2 = 2+3+...+31 Lane3 = 3+...+31 ... Lane30 = 30+31 Lane31 = 31

像一个三角形:

0+1+2+3+...+31 1+2+3+...+31 2+3+...+31 ... 31

所以 Warp Reduction 的经典写法:

for(...)val+=__shfl_down_sync(...);

执行完后:

if(laneId==0){// 使用结果}

因为:

只有 Lane0 = 整个 Warp 的总和

其他 Lane 的值通常直接丢弃,不会再使用。

http://www.jsqmd.com/news/1024955/

相关文章:

  • 数字人直播供应商
  • 大模型GEO主体爱搜索GEO:AI搜索时代的新流量密码,企业如何低成本实现高曝光 - 品牌报告
  • 布局芯片产业参考:2026全年微电子展会排期与精准参展选择方案 - 品牌2026
  • 三步解锁QQ音乐加密文件:让音乐真正属于你的Mac解决方案
  • 河北市政护栏厂家排行:5家符合市政标准的供应商实测盘点 - 奔跑123
  • 3分钟搞定!macOS虚拟PDF打印机终极指南:告别繁琐转换,一键生成PDF
  • 不良资产处置与价值重塑实务白皮书——炜衡品牌的法律服务深度解析与可靠推荐 - 商业科技观察
  • 2026 实测复盘:京东 e 卡免费提现平台深度对比,一文摸清全部扣费套路 - 资讯焦点
  • 2026常州回收名表怎么选|本地高端腕表高折回收机构权威测评榜单 - 名奢变现站
  • 2026常州百达翡丽回收行情解析|本地高折扣正规名表回收机构排行 - 名奢变现站
  • 山东在线检测仪分析仪企业排行:技术与产能双维度盘点 - 奔跑123
  • 成都名表回收必看:哪里能卖得安心又高价?告别套路,这些干货请收好 - 奢侈品回收评测
  • 如何快速掌握SHC脚本加密技术:面向初学者的完整指南
  • 2026年武汉家电维修平台推荐:本地用户反馈靠谱的家电维修服务商-修乐家家电维修 - 资讯纵览
  • 2026年深圳GEO优化公司怎么选?八家优质供应商汇总 - 速递信息
  • 如何使用Flutter与OpenHarmony通信 FlutterChannel
  • 2026年 废旧光伏板热解炉网带推荐榜单:耐用耐高温与高效回收口碑之选 - 品牌发掘
  • 2026报考必看:高考择校怎么选?毕业后好找工作是重点 - 品牌2026
  • 沈阳公安备案名表回收,2026排行榜,行业规范起草单位测评 - 禹竞
  • 抖音直播数据采集完整指南:3步实现实时弹幕监控与分析
  • 邓柏良 “疏肝化瘀三联疗法”:中医治疗肝癌合并顽固性腹水的临床实践
  • PMDARIMA股票预测:自动化ARIMA建模的工程实践指南
  • 零基础拿捏交互式数据大屏!筛选器全局联动+蓝图数据流全流程爆肝详解
  • 2026翡翠回收商户横向测评榜单:合扬实力遥遥领先,六大头部品牌优劣详解 - 开心测评
  • 一台电暖桌的“反季”生意:焱魔方去年卖近18万台,今年备货已抢跑 - 资讯焦点
  • 如何用Python构建抖音直播数据实时监控系统
  • 物理信息神经算子(PINO):融合物理规律与深度学习的创新解决方案
  • 走遍南京全城辖区,2026 高口碑黄金回收门店实力汇总 - 奢侈品回收评测
  • 2026报考必看:一文看懂:西南交大工科实力强不强,王牌专业有哪些 - 品牌2026
  • 2026年度宁波成人学历提升机构综合实力测评:三强揭晓,择校不踩雷