线程间怎么交换数据_在LLVM后端实现跨通道数据搬移

2023-10-26

作者:汪岩

AMD GPU的每个CU有一个64kB的存储空间,称为本地数据共享(Local Data Share,LDS),用于同一计算单元中的work group内各个work item之间的低延迟通信和数据共享。LDS配置为32个bank,每个bank有512个4字节的条目(entry)。尽管有LDS,大多数实际的计算指令仍对寄存器中的数据进行操作。从峰值性能的角度看,global memory带宽比LDS带宽小一个数量级,LDS带宽又比寄存器带宽小一个数量级。从延迟角度看,global memory的延迟约为500 cycles,LDS的延迟约为5 cycles,寄存器的延迟约为1 cycle。因此,寄存器是访问速度最快的存储类型。如果能通过寄存器实现数据共享,相较通过LDS实现数据共享,寄存器无疑是一种更高效的方式。如下图所示,使用LDS需要一次读,一次写,并且需要一个额外的寄存器来保存这些地址。寄存器为线程私有,需要软硬件提供额外的支持,才能实现在寄存器中进行跨通道数据搬移,实现比共享内存更高的效率。CUDA的__shfl_*()和AMD的__amdgcn_ds_*intrinsic函数族为上述功能提供了warp/wavefront级原语软件支持。

1. CUDA中的Warp Shuffle函数定义

为了支持跨通道数据搬移,CUDA在Warp级原语(Warp-Level Primitives)中定义了 Shuffle函数,函数声明如下:

T 

函数声明中的模板参数T可以是int、unsigned int、long、unsigned long、long long、unsigned long long、float或double等。

__shfl_*() intrinsic函数族允许在不使用共享内存的情况下,在warp中的线程之间通过直接读其它线程的寄存器值交换数据。一个warp中包含32通道(lane),每一个线程占用一个通道。交换会同时在warp内的所有活跃线程进行。由参数mask规定哪些线程为活跃线程,mask有32 bit,每个bit对应warp中的一个通道,1表示线程在活跃子集中。如果warp中所有线程都是活跃线程,则mask为0xffffffff。根据类型,每个线程移动4或8字节的数据。这种数据共享方式引入的线程间数据延迟极低,比通过共享内存进行线程间通讯的效果更好、延迟更低,同时也不消耗额外的内存资源来执行数据交换。

线程只能从正在活跃参与__shfl_*()命令的另一个线程中读取数据。如果目标线程处于非活跃状态,则检索到的值不确定。

所有__shfl_*()函数都有一个可选的width参数,该参数会更改内部函数的行为。width必须是2的幂的值。如果width不是2的幂或大于warpSize的数字,则结果不确定。

__shfl_sync()返回同一个warp中ID为srcLane对应的通道(线程)中的var值。如果width小于warpSize,则将warp分割为大小等于width的若干个线程子集,每个子集作为一个单独的实体,其起始逻辑通道ID为0。如果srcLane不在[0:width-1]范围内,则将srcLane对width取模的结果作为lane id,返回的值是对应于该id的通道持有的var值。

下例所示是将通道3中的值33广播到warp中的所有通道,width设为16。

__shfl_up_sync()通过将调用者的通道ID值中减去delta值来计算源通道ID,即,对于处于 x 通道的调用者线程,__shfl_up_sync() 返回在同一个warp中的第 x - delta通道的var值。如果width小于warpSize,则将warp分割为大小等于width的若干个线程子集,每个子集作为一个单独的实体,其起始逻辑通道ID为0。如果源通道ID不在[0:width-1]范围内,源通道ID将不会对width取模。因此,warp中的低端delta个通道中的值保持不变。

下例所示是将通道x-3中的值返回给通道x,width设为16。调用函数后,低端3个通道中的值保持不变。

__shfl_down_sync()通过将调用者的通道ID值中加上delta值来计算源通道ID,即,对于处于 x 通道的调用者线程,__shfl_down_sync() 返回在同一个warp中的第 x + delta通道的var值。如果width小于warpSize,则warp的每个子部分都将作为一个单独的实体,其起始逻辑通道ID为0。如果源通道ID不在[0:width-1]范围内,源通道ID将不会对width取模。因此,warp中的高端delta个通道中的值保持不变。

下例所示是将通道x+3中的值返回给通道x,width设为16。调用函数后,高端3个通道中的值保持不变。

下面的例子使用__shfl_down_sync() ,以tree-reduction方式计算warp中线程val值的和。代码执行后,warp的第一个线程中的val值等于最终的和。

__shfl_xor_sync()通过对调用者的通道ID与laneMask进行按位异或(XOR)运算来计算源通道ID。返回值为计算所得源通道中的var值。此模式实现了蝶形寻址模式。

下例所示是以laneMask=3实现蝶形寻址。例如,对于通道0,0与3的异或结果仍为3。因此,调用函数后,通道0的返回值为通道3中的33;对于通道1,1与3的异或结果为2。因此,调用函数后,通道1的返回值为通道2中的22。

2. __shfl_*()在llvm中的nvvm IR intrinsic定义

NVVM IR是基于LLVM IR的编译器IR(internal representation)。NVVM IR用于表示GPU计算kernel(例如CUDA kernel)。高级语言前端,如CUDA C编译器前端或Clang,都可以生成NVVM IR。相应的,CUDA C中的__shfl_*() intrinsic函数族也被编译器前端翻译为nvvm IR intrinsic函数族:

declare 

llvm.nvvm.shfl.sync.i32是和__shfl_*()对应的nvvm IR intrinsic函数族。其中,%membermask对应__shfl_sync的参数mask。当前正在执行的warp中的每个线程会基于输入参数%b、%c和%mode计算源通道索引j。如果计算出的源通道索引j在范围内,则llvm.nvvm.shfl.sync.i32返回的i32值将是通道j的%a值(即var);否则,返回当前线程的%a值。如果对应通道j的线程处于非活跃状态,则返回的i32值是不确定的。如果源通道j在范围内,则llvm.nvvm.shfl.sync.i32返回的i1值为1,否则为0。

参数%mode必须为常量,不同常量值对应不同shuffle方式:0是IDX,1是UP,2是DOWN,3是BFLY。参数%b根据%mode的值不同指定源通道或源通道偏移。如果%mode为0,对应__shfl_sync(),%b指定源通道;如果%mode为1或2,对应__shfl_up_sync()或__shfl_down_sync(),%b指定源通道偏移(即delta);如果%mode为3,对应__shfl_xor_sync(),%b指定laneMask。

参数%c包含拼凑在一起的两个值。其中一个值是掩码(mask),该值将warp逻辑上分为子段;另一个是钳位值(clamp),用于限制源通道索引的上限。

以下伪代码说明了llvm.nvvm.shfl.sync.i32的语义,从中可以理解上述各个参数的作用和意义:

%

3. DS-Permute Instructions

上述CUDA shuffle intrinsic需要翻译为底层机器指令才能在GPGPU上完成预期功能。AMD Vega开源ISA中包含两条DS-Permute指令:ds_permute_b32和ds_bpermute_b32。下一节会介绍将CUDA shuffle intrinsic翻译为底层(非Nvidia GP)permute指令的方法。这两条指令使用LDS硬件在wavefront的64条通道(lane)之间交换数据,提供了一种不同的方式来表达通道寻址,但并不实际写入LDS位置。ds_permute_b32指令实现前向permute(forward permute),即,将数据放入通道i;而ds_bpermute_b32(在permute之前注意字母“ b”)实现反向permute(backward permute),即,从通道i读取数据。其用法如下:

ds_permute_b32 

其中dest、addr和src是VGPR,addr_offset是可选的立即数偏移量。两条指令均从src中获取数据,并根据提供的地址(addr + addr_offset)对其进行shuffle,并将结果保存到dest寄存器中。整个过程分为两步:第一,所有活跃通道将数据写入临时缓冲区;第二,所有活跃通道从临时缓冲区读取数据,未初始化的位置视为零值。

permute指令在通道之间移动数据,但仍然像其他LDS指令一样使用字节寻址的概念。由于VGPR值的宽度为4个字节,因此addr VGPR中的值应为required_lane_id * 4。

指令在访问临时缓冲区之前将addr_offset立即数添加到addr值。需要注意的是,指令需要一个字节地址,但是只能移动完全对齐的双字。换句话说,仅使用最终地址的位[7:2]。

在许多情况下,permute地址是基于work item ID或lane ID。在kernel执行之前,将work item ID加载到v0(对于多维组,可能会加载到v1和v2)。以下代码将lane ID写入VGPR v6:

v_mbcnt_lo_u32_b32 v6, -1, 0
v_mbcnt_hi_u32_b32 v6, -1, v6

以下是简化的8通道(lane)wavefront的ds_bpermute_b32(反向permute)示例。第一步,所有通道将数据从src写入tmp中的相应位置。第二步,基于addr中的地址从tmp缓冲区读取数据放入dest。图中index的值是取addr[7:2]的结果,表示第二步中tmp元素的实际索引。例如,通道0和1中的addr值分别为8(0b00001000)和9(0b00001001),取[7:2]的结果都为2(0b000010)。可见,虽然通道0和1中的addr值不同,但是,index都指向同一tmp元素。dest中的数据从index标识的通道中读取。例如,dest[0]和dest[1]对应的index都是2,因此,dest[0]和dest[1]的值从通道2读取,读取的值为33。dest[2]对应的index是0。因此,dest[2]的值从通道 0读取,读取的值为11,依此类推。整个过程可理解为dest从lane[index]读数据,即,index控制dest从哪个通道读数据。

在前向permute中,第一步,所有通道均根据addr寄存器中的index将src数据写入tmp缓冲区对应位置。例如,src[0]和src[1]对应的index都是2。两个通道可以写入同一tmp元素,这是ds_bpermute_b32不会出现的情况。前向permute解决这种冲突的方法与写入同一LDS地址的方法相同,即,ID较大的通道获胜。因此,在这个例子中,tmp[2]最后保留的值为10。在第二步中,dest直接读取tmp对应位置的数据。整个过程可以理解为src向lane[index]写数据,即,index控制src向哪个通道写数据。

与CUDA的__shfl_*() intrinsic函数族类似,AMD的HCC编译器为ds_permute和ds_bpermute提供intrinsic支持。这些是device function(标记为[[hc]]),因此可以从kernel(在hcc上运行)调用:

extern 

3.将nvvm IR intrinsic映射为自定义intrinsic或AMDGPU intrinsic

nvvm IR intrinsic针对nvdia GPU独家设计。虽然llvm中公开了nvvm IR intrinsic定义形式,但机器指令实现细节不可能公开。为了将__shfl_*()的nvvm IR intrinsic翻译为机器指令,首先要了解nvvm IR intrinsic的定义形式。__shfl_*()的nvvm IR intrinsic定义在llvmincludellvmIRintrinsicNVVM.td文件中,此处仅以__shfl_sync()为例说明。

def 

为了在非Nvdia GPU(AMD GPU或专有GPU)上兼容nvvm IR intrinsic功能,需要在AMDGPU后端或专有GPU后端,将nvvm IR intrinsic翻译为AMDGPU intrinsic或自定义intrinsic。本节以自定义intrinsic为例介绍相关实现流程,AMDGPU intrinsic实现流程与此类似。

a. 定义自定义intrinsic。

在llvm/include/llvm/IR/Intrinsics<target>.td中增加定义:

def 

b. 定义instrinsic对应的SDNode。

def 

在llvm/lib/Target/<target>/<target>ISelLowering.cpp中增加如下代码:

case 

c. 将nvvm intrinsic换成自定义intrinsic或AMDGPU intrinsic。

bool 

d. 定义intrinsic到Machine code之间的match patten。

在llvm/lib/Target/<target>/<target>InstructionsDSlike.td中增加如下代码:

class 

参考文献:

[1] https://docs.nvidia.com/cuda/cuda-c-programming-guide/#warp-shuffle-functions

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

线程间怎么交换数据_在LLVM后端实现跨通道数据搬移 的相关文章

  • 练手小项目:51单片机控制的智能台灯设计(自动感应调光) 电路图,测试图,源代码全技术资料

    功能及概述 本系统组成如图一所示 主要由三部分组成 传感器及信号处理部分 检测人体辐射红外信号及光强信号经过处理后变成可处理的数字信号 以80C51组成的中央处理单元 处理信号并发出控制命令 提醒电路及灯光控制电路 给出提醒信号并根据80C
  • vue3基础 ---- 上

    目录 一 vue3介绍 1 官网初识 2 环境搭建 2 1 线上尝试 2 2 CDN使用 2 3 Vue CLI 2 4 Vite 二 vue3基础 1 模板语法 1 1 我的第一个vue应用 1 2 应用背后的真相 1 3 模板语法 新的
  • API 治理的目标是什么?

    建立有效的API治理需要正确理解其目标 但它究竟是什么呢 是定义标准或规则并应用它们吗 都不是 虽然这些是治理的一个重要手段 但这并非其最终目的 为了揭示API治理的真正目标 让我们探讨一下在适当地制定标准后能得到什么 1 从 API 混乱
  • Jetty服务器好处

    Jetty可以同时处理大量连接而且可以长时间保持连接 适合于web聊天应用等等 Jetty的架构简单 因此作为服务器 Jetty可以按需加载组件 减少不需要的组件 减少了服务器内存开销 从而提高服务器性能 Jetty默认采用NIO结束在处理
  • python中sys是什么意思_python里的sys是什么意思

    sys模块功能多 我们这里介绍一些比较实用的功能 相信你会喜欢的 和我一起走进python的模块吧 sys模块的常见函数列表 sys argv 实现从程序外部向程序传递参数 sys exit arg 程序中间的退出 arg 0为正常退出 s
  • ubuntu配置mysql网络连接_在Ubuntu14.04中配置mysql远程连接教程

    上一篇文章 小编带大家学会了在Ubuntu14 04中安装MySQL 没有来得及上课的小伙伴们可以戳这篇文章 如何在Ubuntu14 04中安装mysql 今天给大家分享一下 如何简单的配置MySQL 可以实现远程连接 具体的教程如下 1
  • 代码雨类库的实现

    代码雨类库的实现 电影黑客帝国有个代码雨效果 挺酷的 我在网上看到了使用js写的代码雨的代码 我把由函数实现的代码 改为使用类实现代码雨特效 一 设计一个简单美化的网页且包含该有的功能 功能 1 一块画布 实现代码雨的展示 2 初始化按钮
  • 字节跳动第五届青训营后端练习题——分割ip(Java版)

    题目 有效 IP 地址正好由四个整数 每个整数位于 0 到 255 之间组成 且不能含有前导 0 整数之间用 分隔 例如 0 1 2 201 和 192 168 1 1 是有效 IP 地址 但是 0 011 255 245 192 168
  • nginx 反向代理常用配置

    全部代理 location 设置跨域 add header Access Control Allow Origin add header Access Control Allow Methods GET POST OPTIONS add h
  • TS2559: Type ‘{ children: string; }‘ has no properties in common with type ‘IntrinsicAttributes & Fi

    Type children string key string is not assignable to type IntrinsicAttributes FilterTagPropsType Property children does
  • 锁与事务的关系

    在并发场景下 我们往往需要在事务方法中加锁来应对并发 如下 下面以 ReentrantLock 为例子 public final static ReentrantLock MY LOCK new ReentrantLock Transact
  • ubuntu安装ssh无法连接解决日志(已解决,可连接)

    原文链接http bbs chinaunix net thread 3585704 1 1 html 网上有很多介绍在Ubuntu下开启SSH服务的文章 但大多数介绍的方法测试后都不太理想 均不能实现远程登录到Ubuntu上 最后分析原因是
  • SpringBoot项目配置全局处理异常

    1 自定义异常 自定义异常 public class RRException extends RuntimeException private static final long serialVersionUID 1L private St
  • k8s学习

    主节点配置一定要好 K8S学习之路 1 介绍 1 1单机部署 1 2 虚拟化部署 类似window上安装多个linux虚拟机 在虚拟机中部署程序 使得程序之间不会互相影响 1 3 容器化部署 共享了操作系统 保证每个系统拥有自己的文件系统
  • MySQL-binlog2sql:非主从关系实现数据的【数据同步+数据恢复+数据追踪】

    文章目录 MySQL binlog2sql 非主从实时同步 恢复误删数据 1 引 1 介绍 2 功能 3 针对3种场景 4 脚本汇总说明 2 先决条件 1 安装 MySQL 2 修改 MySQL 配置 3 安装 binlog2sql 1 解
  • yii2 mysql设置时区

    第一步 修改配置文件 common config db php 注 8 00为北京时间 Asia Shanghai common config main php 第二步 修改vendor yiisoft yii2 db Connection
  • 抓取网站中的视频

    最近想从别人家的网站宣传片上提取一些素材 借鉴一下 之前也没有弄过 但是我的思路就是从网页的缓存中查找播放完后缓存的视频 然后失败了 然后又想到了网页打开源代码 然后查找到网页源代码饮用的视频的路径 然后找到视频 然后 再次失败 网上找了好
  • css基础———清除浮动的一些方法及区别

    为什么要清楚浮动 地址 http blog csdn net qwe502763576 article details 78811658 清除浮动方法概览 这里例举四种常见的清除浮动方式 方式一 使用overflow属性来清除浮动 ovh
  • 论文阅读

    简介 paper https arxiv org abs 1911 11907 github https github com huawei noah ghostnet Ghostnet CVPR2020 是华为提出的一种轻量级网络 结构类

随机推荐

  • WSL安装

    WSL安装教程 WSL简介 Windows Subsystem for Linux 简称WSL 是一个在Windows10上能够运行原生Linux二进制可执行文件 ELF格式 的兼容层 它是有微软与Canonical公司合作开发 其目标正是
  • 模糊查询与带参数跳转

    一 模糊查询 使用
  • 方法重写(override)原则

    方法的重写 override 两同两小一大原则 1 方法名相同 参数类型相同 2 子类返回类型小于等于父类方法返回类型 3 子类抛出异常小于等于父类方法抛出异常 4 子类访问权限大于等于父类方法访问权限
  • oracle RAC ORA-03113 错误解决

    好久 没有更新博客 太懒了 这咋换工作呢 1 错误现象 数据库 客户端连接不正常 频繁报 ORA 03113 错误 oracle 文档中对这个错误这样解释 ORA 03113 错误就是说连接到数据库的网络中断了 有些错误由于频繁出现 原因复
  • res_company_white_url.py 详解

    res company white url py 主要作用是 在数据库中建立一个表 存放白名单的URL 当我们读取文件时 先判断Referer是否在白名单中 如果不在则自动转到一个图片文件 防止盗链 接下来我们看一下主要代码 class C
  • unexpected keyword argument 'renderer'-DjangoUeditor

    今天在集成DjangoUeditor按照官方的Github集成之后 本以为就可以看到后台了没想到直接报错 render got an unexpected keyword argument renderer 报错93行 boundfield
  • 【QT】——06_带参数的信号(笔记)

    信号重载 说明 信号是可以重载的 相同的名字不同的参数 在发射信号的时候给值 emit musicSignal 100 音乐菜单 主窗口 h 创建一个带参的槽来处理信号 注意槽的参数要与信号一致 void dealMusic2 int QS
  • 《Hadoop学习笔记系列》二.Hadoop分布式文件系统 HDFS

    0 Hadoop分布式文件系统 HDFS HDFS以流式数据访问模式来存储超大文件 运行与商用硬件集群上 1 流式数据访问 HDFS的构建思路 一次写入 多次读取是最高效的访问模式 2 Block数据块 HDFS基本读写单位 类似于磁盘的页
  • STM32的ADC采样与多通道ADC采样

    一 单通道采样 参考资料 STM32库开发实战指南 刘火良 杨森著 原理性质的东西还是少讲 因为上面那本书里面讲解的很详细了 直接来看硬件电路图 这里使用的是3362电位器 10K 即用STM32来测量PB0和GND两端的电压 这样的电路设
  • 一篇明白SQL的执行顺序

    这是一条标准的查询语句 这是我们实际上SQL执行顺序 我们先执行from join来确定表之间的连接关系 得到初步的数据 where对数据进行普通的初步的筛选 group by 分组 各组分别执行having中的普通筛选或者聚合函数筛选 然
  • 小谈HashMap与ConcurrentHashMap

    HashMap JDK7 在JDK7中 HashMap通过数组加链表的形式存储 当元素个数达到阈值 并且数组下标已经存在元素 则会进行扩容 如果数组下标不存在元素 则直接添加 不会扩容 JDK7中添加元素使用的是头插法 在高并发的环境下可能
  • [算法通关村] 1.3 链表的删除

    上一节我们谈到了链表的头插 尾插 中间插入的方法 忘记的小伙伴可以复习一下 算法通关村 1 2 链表的插入 接下来 完成链表的删除工作 我们在上一节的学习中 分别在链表的开头 中间和结尾插入了节点 现在我们想使链表恢复原来的样子 即 1 g
  • 更多的服务商从业者都开始关注刷脸支付

    刷脸支付就是通过人脸进行支付 简单来说消费者在接入蜻蜓的商家结账付款时 只需将脸面对蜻蜓上的摄像头即可完成支付 在这一过程中用户甚至不需要用手机 支付效率相比扫码支付更加的高效 此外由于支付宝刷脸支付是基于3D人脸识别技术 在安全也要比扫码
  • 【机器学习】编码、创造和筛选特征

    在机器学习和数据科学领域中 特征工程是提取 转换和选择原始数据以创建更具信息价值的特征的过程 假设拿到一份数据集之后 如何逐步完成特征工程呢 文章目录 一 特性类型分析 1 1 数值型特征 1 2 类别型特征 1 3 时间型特征 1 4 文
  • SpringMVC的全注解开发

    文章目录 一 spring mvc xml 中组件转化为注解形式 二 DispatcherServlet加载核心配置类 三 消除web xml 一 spring mvc xml 中组件转化为注解形式 跟之前全注解开发思路一致 xml配置文件
  • 2.c语言中将两个整数相加

    上一个文章 我讲述了如何输出文字 接下来我就来讲讲有一丢丢难度的算法了 这个算法就是学习如何将两个整数相加 这是学习如何编写一个计算机软件的第一步 现在我就为你们来一一讲解 代码如下 include
  • StringBuffer integer

    StringBuffer 一 1 StringBuffer是一个容器 而容器的特点是可以修改 基本操作是增添删改 这也是它与StringBuffer的主要区别 2 线程安全 可变的字符序列 是一个字符缓冲区 也是final型 不能被继承 3
  • java基础-垃圾收集器及其回收算法的介绍

    文章目录 前言 一 垃圾收集器管理的区域 二 垃圾收集器的回收步骤 1 阶段一 判断对象是否存活 2 阶段二 筛选 三 垃圾收集算法 1 分代收集理论 理论基础 2 标记 清除算法 3 标记 复制算法 4 标记 整理算法 四 补充 前言 最
  • v8所有例子在里面

    https github com nodejs nan blob master doc methods md
  • 线程间怎么交换数据_在LLVM后端实现跨通道数据搬移

    作者 汪岩 AMD GPU的每个CU有一个64kB的存储空间 称为本地数据共享 Local Data Share LDS 用于同一计算单元中的work group内各个work item之间的低延迟通信和数据共享 LDS配置为32个bank