CUDA之Warp Shuffle详解

2023-11-09

之前我们有介绍shared Memory对于提高性能的好处,在CC3.0以上,支持了shuffle指令,允许thread直接读其他thread的寄存器值,只要两个thread在 同一个warp中,这种比通过shared Memory进行thread间的通讯效果更好,latency更低,同时也不消耗额外的内存资源来执行数据交换。主要包含如下API:

[cpp]  view plain  copy
  1. int __shfl(int var, int srcLane, int width=warpSize);   
[cpp]  view plain  copy
  1. int __shfl_up(int var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. int __shfl_down(int var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. int __shfl_xor(int var, int laneMask, int width=warpSize);   
[cpp]  view plain  copy
  1. float __shfl(float var, int srcLane, int width=warpSize);   
[cpp]  view plain  copy
  1. float __shfl_up(float var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. float __shfl_down(float var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. float __shfl_xor(float var, int laneMask, int width=warpSize);   
[cpp]  view plain  copy
  1. half __shfl(half var, int srcLane, int width=warpSize);   
[cpp]  view plain  copy
  1. half __shfl_up(half var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. half __shfl_down(half var, unsigned int delta, int width=warpSize);   
[cpp]  view plain  copy
  1. half __shfl_xor(half var, int laneMask, int width=warpSize);  

这里介绍warp中的一个概念lane,一个lane就是一个warp中的一个thread,每个lane在同一个warp中由lane索引唯一确定,因此其范围为[0,31]。在一个一维的block中,可以通过下面两个公式计算索引:

laneID = threadIdx.x % 32

warpID = threadIdx.x / 32

例如,在同一个block中的thread1和33拥有相同的lane索引1。

Variants of the Warp Shuffle Instruction

有两种设置shuffle的指令:一种针对整型变量,另一种针对浮点型变量。每种设置都包含四种shuffle指令变量。为了交换整型变量,使用过如下函数:

int __shfl(int var, int srcLane, int width=warpSize);

该函数的作用是将var的值返回给同一个warp中lane索引为srcLane的thread。可选参数width可以设置为2的n次幂,n属于[1,5]。

eg:如果shuffle指令如下:

int y = shfl(x, 3, 16);

则,thread0到thread15会获取thread3的数据x,thread16到thread31会从thread19获取数据x。

当传送到shfl的lane索引相同时,该指令会执行一次广播操作,如下所示:

 

另一种使用shuffle的形式如下:

int __shfl_up(int var, unsigned int delta, int width=warpSize)

该函数通过使用调用方的thread的lane索引减去delta来计算源thread的lane索引。这样源thread的相应数据就会返回给调用方,这样,warp中最开始delta个的thread不会改变,如下所示:

 

第三种shuffle指令形式如下:

int __shfl_down(int var, unsigned int delta, int width=warpSize)

该格式是相对__shfl_down来说的,具体形式如下图所示:

 

最后一种shuffle指令格式如下:

int __shfl_xor(int var, int laneMask, int width=warpSize)

这次不是加减操作,而是同laneMask做抑或操作,具体形式如下图所示:

 

所有这些提及的shuffle函数也都支持单精度浮点值,只需要将int换成float就行,除此外,和整型的使用方法完全一样。

我们这里以reduction为例,看一下相比于使用shared memory进行通信的性能差异。

算法背景:为了简单起见,我们计算每32个int型变量元素的元素和。假设一个数组包含n个元素(e.g. n = 1 << 20),每32个元素计算一个和,则输出结果为n/32个int型变量。在编程中,block的大小就是32(刚好是一个warp),grid的大小是n / 32。

第一种,利用shared memory进行reduction:

[cpp]  view plain  copy
  1. __global__ void reduce0(int *dst, int *src, const int n) {  
  2.     __shared__ int sdata[WARP_SIZE*2];  
  3.     int tidGlobal = threadIdx.x + blockDim.x * blockIdx.x;  
  4.     int tidLocal = threadIdx.x;  
  5.   
  6.     sdata[tidLocal] = src[tidGlobal];  
  7.   
  8.     //actually the sync is no need here because only a warp exists in a block.  
  9.     __syncthreads();  
  10.   
  11.     //only 32 threads in a block  
  12.     //reduce in a warp  
  13.     if (tidLocal < 32)  
  14.         sdata[tidLocal] += sdata[tidLocal+16];  
  15.     __syncthreads();  
  16.     if (tidLocal < 32)  
  17.         sdata[tidLocal] += sdata[tidLocal+8];  
  18.     __syncthreads();  
  19.     if (tidLocal < 32)  
  20.         sdata[tidLocal] += sdata[tidLocal+4];  
  21.     __syncthreads();  
  22.     if (tidLocal < 32)  
  23.         sdata[tidLocal] += sdata[tidLocal+2];  
  24.     __syncthreads();  
  25.     if (tidLocal < 32)  
  26.         sdata[tidLocal] += sdata[tidLocal+1];  
  27.     __syncthreads();  
  28.   
  29.     if (tidLocal == 0)  
  30.         dst[blockIdx.x] = sdata[0];  
  31.   
  32. }  
几点说明:

  1. 为了使warp内没有分支,32个线程都做加法操作(多分配点shared memory空间即可)。
  2. 一个warp内的32个线程执行是同步的,因此不用担心写后读的错误。
  3. 其实,一个block内只有一个warp,因此,所有的同步函数在这里都可以省略,条件语句if(tidLocal < 32)也可以省略。

第二种,利用shuffle进行通信:

[cpp]  view plain  copy
  1. __global__ void reduce1(int *dst, int *src, const int n) {  
  2.     int tidGlobal = threadIdx.x + blockDim.x * blockIdx.x;  
  3.     int tidLocal = threadIdx.x;  
  4.   
  5.     int sum = src[tidGlobal];  
  6.     //actually the sync is no need here because only a warp exists in a block.  
  7.     __syncthreads();  
  8.   
  9.     for (int offset = WARP_SIZE/2; offset > 0; offset /= 2) {  
  10.         sum += __shfl_down(sum, offset);  
  11.     }  
  12.   
  13.     if (tidLocal == 0)  
  14.         dst[blockIdx.x] = sum;  
  15.   
  16. }  
几点说明:

  1. 我们利用shuffle来做warp内的通信,因此没有用到shared memory。
  2. 关于shuffle的操作含义,可以参考"cuda programming guide".

性能测试:

利用nvvp,我们来分析一下两个kernel的执行时间:

[cpp]  view plain  copy
  1. ==31758== NVPROF is profiling process 31758, command: ./a.out  
  2. Device 0: "Tesla K20c"  
  3. check right!  
  4. check right!  
[cpp]  view plain  copy
  1. ==31758== Profiling application: ./a.out  
  2. ==31758== Profiling result:  
  3. Time(%)      Time     Calls       Avg       Min       Max  Name  
  4.  80.87%  2.5935ms         1  2.5935ms  2.5935ms  2.5935ms  [CUDA memcpy HtoD]  
  5.   8.07%  258.76us         1  258.76us  258.76us  258.76us  reduce0(int*, int*, int)  
  6.   6.02%  192.90us         1  192.90us  192.90us  192.90us  reduce1(int*, int*, int)  
  7.   5.04%  161.73us         2  80.866us  80.866us  80.866us  [CUDA memcpy DtoH]  
我们可以看到,在这个小例子中,使用shuffle可以提升性能25%左右。除了可以利用shuffle来做warp内的reduction操作,还可以进行scan,broadcast等操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA之Warp Shuffle详解 的相关文章

  • 加速Cuda程序

    要更改哪一部分来加速此代码 代码到底在做什么 global void mat Matrix a Matrix b int tempData new int 2 tempData 0 threadIdx x tempData 1 blockI
  • 使用 CUDA 进行逐元素向量乘法

    我已经在 CUDA 中构建了一个基本内核来执行逐元素两个复向量的向量 向量乘法 内核代码插入如下 multiplyElementwise 它工作正常 但由于我注意到其他看似简单的操作 如缩放向量 在 CUBLAS 或 CULA 等库中进行了
  • CUDA、NPP 滤波器

    CUDA NPP 库支持使用 nppiFilter 8u C1R 命令过滤图像 但不断出现错误 我可以毫无问题地启动并运行 boxFilterNPP 示例代码 eStatusNPP nppiFilterBox 8u C1R oDeviceS
  • 如何为 CUDA 内核选择网格和块尺寸?

    这是一个关于如何确定CUDA网格 块和线程大小的问题 这是对已发布问题的附加问题here https stackoverflow com a 5643838 1292251 通过此链接 talonmies 的答案包含一个代码片段 见下文 我
  • 内联 PTX 汇编代码强大吗?

    我看到一些代码示例 人们在 C 代码中使用内联 PTX 汇编代码 CUDA工具包中的文档提到PTX很强大 为什么会这样呢 如果我们在 C 代码中使用这样的代码 我们会得到什么好处 内联 PTX 使您可以访问未通过 CUDA 内在函数公开的指
  • cuda 文件组织的有效方式:.cpp .h .cu .cuh .curnel 文件

    cuda最容易理解 最高效的代码组织是什么 经过一番调查后 我发现 cuda 函数声明应位于 cuh 文件中 实现位于 cu 文件中 内核函数实现位于 curnel 文件中 其他 C 内容通常在 cpp 和 h 文件中 最近我发布了一个问题
  • cudaDeviceScheduleBlockingSync 和 cudaDeviceScheduleYield 之间有什么区别?

    正如这里所说 如何减少 CUDA 同步延迟 延迟 https stackoverflow com questions 11953722 how to reduce cuda synchronize latency delay 等待设备结果有
  • CUDA NSight 未随 Windows 8 上的 CUDA 5.0 安装文件一起安装? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 据我所知 Nvidia 网站上没有 Nsight Eclipse 的下载链接 它说它将由 CUDA 5 安装本机安装 但并没有随CUDA安装一起安装
  • 如何在CUDA应用程序中正确应用线程同步?

    一般来说 我在应用程序中偶尔会使用线程同步 因为我并不经常需要此功能 我并不是真正的高级 C C 程序员 但我也不是初学者 我开始学习 CUDA C 对当今 GPU 与 CPU 的能力相比感到兴奋 我意识到 CUDA 编程主要是关于并行线程
  • CUDA 添加矩阵的行

    我试图将 4800x9600 矩阵的行加在一起 得到一个 1x9600 的矩阵 我所做的是将 4800x9600 分成 9 600 个矩阵 每个矩阵长度为 4800 然后我对 4800 个元素进行缩减 问题是 这真的很慢 有人有什么建议吗
  • NVCC 警告级别

    我希望 NVCC 将以下警告视为错误 warning calling a host function foo from a host device function bar NVCC 文档 NVIDIA CUDA 编译器驱动程序 NVCC
  • CUDA 的嵌套循环

    我想将我的 C 代码移植到 CUDA 主要计算部分包含3个for嵌套循环 for int i 0 i lt Nx i for int j 0 j
  • Cuda:最小二乘求解,速度较差

    最近 我使用Cuda编写了一个名为 正交匹配追踪 的算法 在我丑陋的 Cuda 代码中 整个迭代需要 60 秒 而 Eigen lib 只需 3 秒 在我的代码中 矩阵 A 是 640 1024 y 是 640 1 在每一步中 我从 A 中
  • 为什么 cudaGLSetGLDevice 失败,即使它是在 main 函数的第一行中调用的

    我想使用 OpenGL 和 CUDA 之间的互操作性 我知道 正如一些教程所说 第一步是选择设备 但是 当我在主函数的第一行中调用 cudaGLSetGLDevice 0 时 程序退出并显示信息 cudaSafeCall 运行时 API 错
  • 将内核链接到 PTX 函数

    我可以使用 PTX 文件中包含的 PTX 函数作为外部设备函数 将其链接到另一个应调用该函数的 cu 文件吗 这是另一个问题CUDA 将内核链接在一起 https stackoverflow com questions 20636800 c
  • 使用推力来处理 CUDA 类中的向量?

    我对 C 类的推力的适用性有疑问 我正在尝试实现一个类对象 该对象接收顶点的 x y z 坐标作为 ver1 ver2 和 ver3 然后 分配给一个三角形并计算面积和法向量 然而 我不太明白如何创建一类推力向量 这是我从文件中读取的顶点坐
  • 布尔实现的atomicCAS

    我想弄清楚是否存在错误答案 https stackoverflow com a 57444538 11248508 现已删除 关于Cuda like的实现atomicCAS for bool是 答案中的代码 重新格式化 static inl
  • 为什么使用 boost::none 无法通过 nvcc 编译?

    我正在尝试编译以下代码 include
  • 了解流式多处理器 (SM) 和流式处理器 (SP)

    我正在尝试了解 GPU 的基本架构 我已经阅读了很多材料 包括这个非常好的答案 https stackoverflow com a 2213744 2386113 但我仍然很困惑 无法得到一个好的图片 我的理解 GPU 包含两个或多个流式多
  • 如何转储所有 NVCC 预处理器定义?

    我想达到同样的效果 gcc dM E lt dev null 如所描述的here https stackoverflow com q 2224334 1593077 但对于 nvcc 也就是说 我想转储所有 nvcc 的预处理器定义 唉 n

随机推荐

  • 使用HttpClient下载网页

    Httpclient是一个非常好用的第三方库 用于网络编程 可以用来做个爬虫程序什么之类的 安卓中内置的网络编程库就是httpclient 下面就可大家介绍介绍怎么使用httpclient下载新浪首页的源代码 其过程就是首先构建一个http
  • python怎么调用文件_Python如何调用m文件

    Python如何调用m文件 一 安装Python 并正确配置环境变量 matlab2016a只支持python2 7 python3 3 python3 4 python3 4以上版本不支持 推荐学习 Python教程 二 安装Matlab
  • CSS中如何实现一个自适应正方形(宽高相等)的元素?

    聚沙成塔 每天进步一点点 专栏简介 利用 padding 百分比 2 利用 before 伪元素 写在最后 专栏简介 前端入门之旅 探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅
  • cocos2dx中的内存加载PLIST

    今天 加载图片时有问题 myButtonPList loadTextures jineng 02103 png jineng 02103 light png jineng 03101 png UI TEX TYPE PLIST myButt
  • 时间趋势可视化-柱形图

    第1关 大胃王 比赛数据柱形图绘制 绘制柱形图的基本步骤 本关任务 根据实训提供的 大胃王 比赛数据绘制柱形图 熟悉柱形图绘制的基本步骤 coding utf 8 import pandas as pd from matplotlib im
  • 利用CIBERSORT免疫细胞类群分析详细教程

    利用CIBERSORT免疫细胞类群分析详细教程 现在最火的组学技术是什么 无疑便是单细胞测序了 通过单细胞测序 科研人员可以获得比原来更为精细的细胞图谱 但是单细胞测序诸多限制条件 也是不能让大家很好地利用这项技术解决自己的科学问题 除了较
  • 【Qt】通过QtCreator源码学习Qt(十二):Q_D和Q_Q指针(简称“d指针”)详解

    1 Q D和Q Q指针 简称 d指针 简介 参考博客 https www devbean net 2016 11 qt creator source study 07 https blog csdn net rabinsong articl
  • SpringBoot项目中统计所有Controller中的方法

    对接口方法进行抽象 Data public class ControllerMethodItem public String controllerName public String methodName public String req
  • vscode中preLaunchTask“g++”已终止,退出代码为1的解决方案

    问题背景 楼主原来做的项目 电脑中装了MinGW64 还有MinGW的32位版在用vscode时发现出现了 preLaunchTask g 已终止 退出代码为1的问题 找了好久 解决了问题 launch json 注释的位置 这里修改GDB
  • Vue中实现放大镜效果

    先来看一下我们需要实现的效果是怎样的 这里我们没有使用原生的 js 方法去实现 而是使用的 Vue3 官方推荐的一个工具库 vueuse cor 中的 useMouseInElement 方法来实现放大镜的效果 首先来看一下 useMous
  • 如何安装和配置树莓派

    如何安装和配置树莓派 如果你有一块树莓派的板子 还有一个没安装系统的SD卡 怎么能把系统装上 配置好跑起来 这篇文章主要就讲这个事 这是一块Raspberry Pi Zero W板 以及一个空SD卡 当然 我们需要一个SD卡读卡器 还需要一
  • Flink Native Kubernetes (一)

    目录 文章目录 目录 概述 Linux 集群描述 版本 部署K8S环境 配置Yum 安装docker 安装Rancher 安装K8s 工作集群 添加KubeCtl命令上下文 运行FlinkDemo FlinkSession关于K8s的基础环
  • 三:Sensor SLPI层代码分析---

    三 Sensor SLPI层代码分析 在学习SLPI侧代码前我们先了解下SEE的registry config registry 放在 persist sensors registry registry中 它是通过config生成的 是给S
  • 循环遍历本地的图片使用BASE64编码,并在ajax也遍历图片

    前端调用ajax到后端去图片的方法 并返回 public void search HttpServletRequest request HttpServletResponse response throws Exception String
  • 【毕业设计】基于stm32的智能扫地机器人设计与实现 - 单片机 物联网

    文章目录 0 简介 1 课题背景 2 硬件系统总体框架 2 1 电机驱动 2 2 红外线传感器 2 3 超声波传感器 2 4 MPU6050 2 5 ATK ESP8266 WI FI 模块 2 6 电源管理模块 3 软件系统设计 3 1
  • 前端知识点

    写在前面 CSDN话题挑战赛第1期 活动详情地址 CSDN 参赛话题 前端面试宝典 话题描述 欢迎各位加入话题创作得小伙伴 如果我没有猜错得话 我觉得你是应该同我一样是一位前端人 如今前端在IT事业中的占比越来越重 已经成为不可缺少的部分
  • 2019年DNS服务器速度排行榜

    第一名 DNSPod 不得不说腾讯自从收购了DNSPod后 无论是服务还是速度都有显著的提升 无论是访问速度还是解析速度都在国内是处于龙头大哥的地位 昔日的老大114的地位已经不保 作为腾讯旗下的公司 在游戏解析这一块来说 技术自然是领先于
  • 排序算法详解(堆,归并,快速排序最简及理解写法)

    十大排序算法和复杂度 常见排序的详解 只讲解真实场景中常用的 简单的就不分析了大家稍微看一下就行 快速排序 快排的思想主要就是每次把一个位置放好后 可以把数组分成两半 递归处理子问题即可 空间复杂度OlogN 分析 每次都分成两半处理子问题
  • IDEA报错程序包xxx不存在,但Depandencies依赖里明明有

    IDEA报错程序包xxx不存在 但依赖里明明有 看一下这个项目的pom xml 我这边引用的是公共依赖 应该是运行的时候依赖没有引用过来 搞了半天 网上搜了很多没搜到 后来我把 settings gt Runner 设置调了一下 就没有问题
  • CUDA之Warp Shuffle详解

    之前我们有介绍shared Memory对于提高性能的好处 在CC3 0以上 支持了shuffle指令 允许thread直接读其他thread的寄存器值 只要两个thread在 同一个warp中 这种比通过shared Memory进行th