CUDA——向量化内存

news2025/7/21 4:26:24

许多 CUDA 内核受带宽限制,新硬件中触发器与带宽的比率增加导致更多带宽受限内核。 这使得采取措施缓解代码中的带宽瓶颈变得非常重要。 在本文中,我将向您展示如何在 CUDA C/C++ 中使用矢量加载和存储来帮助提高带宽利用率,同时减少执行指令的数量。

让我们从以下简单的内存复制内核开始。

__global__ void device_copy_scalar_kernel(int* d_in, int* d_out, int N) { 
  int idx = blockIdx.x * blockDim.x + threadIdx.x; 
  for (int i = idx; i < N; i += blockDim.x * gridDim.x) { 
    d_out[i] = d_in[i]; 
  } 
} 

void device_copy_scalar(int* d_in, int* d_out, int N) 
{ 
  int threads = 128; 
  int blocks = min((N + threads-1) / threads, MAX_BLOCKS);  
  device_copy_scalar_kernel<<<blocks, threads>>>(d_in, d_out, N); 
}

在此代码中,我使用了grid-stride loops,如较早的 earlier CUDA Pro Tip post. 中所述。 图 1 显示了以 GB/s 为单位的内核吞吐量作为 copy size的函数。

Figure 1: Copy bandwidth as a function of copy size.Figure 1: Copy bandwidth as a function of copy size

我们可以使用 CUDA 工具包中包含的 cuobjdump 工具检查此内核的程序集。

cuobjdump 从 CUDA 二进制文件(独立的和嵌入在主机二进制文件中的文件)中提取信息,并以人类可读的格式呈现它们。 cuobjdump 的输出包括每个内核的 CUDA 汇编代码、CUDA ELF 部分标题、字符串表、重定位器和其他 CUDA 特定部分。 它还从主机二进制文件中提取嵌入的 ptx 文本。
有关每个 GPU 架构的 CUDA 汇编指令集的列表,请参阅指令集参考。

 %> cuobjdump -sass executable

scalar copy kernel主体的 SASS 如下:

/*0058*/ IMAD R6.CC, R0, R9, c[0x0][0x140]                
/*0060*/ IMAD.HI.X R7, R0, R9, c[0x0][0x144]              
/*0068*/ IMAD R4.CC, R0, R9, c[0x0][0x148]               
/*0070*/ LD.E R2, [R6]                                   
/*0078*/ IMAD.HI.X R5, R0, R9, c[0x0][0x14c]              
/*0090*/ ST.E [R4], R2

在这里我们可以看到总共有六个与复制操作相关的指令。四个 IMAD 指令计算加载和存储地址,LD.EST.E 从这些地址加载和存储 32 位。

我们可以通过使用矢量化加载和存储指令 LD.E.{64,128}ST.E.{64,128} 来提高此操作的性能。这些操作还加载和存储数据,但以 64 位或 128 位宽度执行。使用矢量化加载可减少指令总数、减少延迟并提高带宽利用率。

使用向量化加载的最简单方法是使用 CUDA C/C++ 标准头文件中定义的向量数据类型,例如 int2、int4float2。您可以通过 C/C++ 中的类型转换轻松使用这些类型。例如,在 C++ 中,您可以使用 reinterpret_cast<int2*>(d_in) 将 int 指针 d_in 重铸为 int2 指针。在 C99 中,您可以使用转换运算符来做同样的事情:(int2*(d_in))

取消引用这些指针将导致编译器生成矢量化指令。但是,有一个重要警告:这些指令需要对齐的数据。设备分配的内存自动对齐到数据类型大小的倍数,但如果您偏移指针,偏移量也必须对齐。例如,reinterpret_cast<int2*>(d_in+1) 无效,因为 d_in+1 未与 sizeof(int2) 的倍数对齐。

如果使用“对齐”偏移量,则可以安全地偏移数组,如 reinterpret_cast<int2*>(d_in+2)。您还可以使用结构生成矢量化负载,只要该结构的大小是两个字节的幂。

struct Foo {int a, int b, double c}; // 16 bytes in size
Foo *x, *y;
…
x[i]=y[i];

现在我们已经了解了如何生成向量化指令,让我们修改内存复制内核以使用向量加载。

__global__ void device_copy_vector2_kernel(int* d_in, int* d_out, int N) {
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  for (int i = idx; i < N/2; i += blockDim.x * gridDim.x) {
    reinterpret_cast<int2*>(d_out)[i] = reinterpret_cast<int2*>(d_in)[i];
  }

  // in only one thread, process final element (if there is one)
  if (idx==N/2 && N%2==1)
    d_out[N-1] = d_in[N-1];
}

void device_copy_vector2(int* d_in, int* d_out, int n) {
  threads = 128; 
  blocks = min((N/2 + threads-1) / threads, MAX_BLOCKS); 

  device_copy_vector2_kernel<<<blocks, threads>>>(d_in, d_out, N);
}

这个内核只有一些变化。 首先,循环现在只执行 N/2 次,因为每次迭代处理两个元素。 其次,我们在文案中使用了上面描述的铸造技术。 第三,我们处理 N 不能被 2 整除时可能出现的任何剩余元素。最后,我们启动的线程数是我们在标量内核中执行的线程数的一半。

检查 SASS 我们看到以下内容。

/*0088*/                IMAD R10.CC, R3, R5, c[0x0][0x140]              
/*0090*/                IMAD.HI.X R11, R3, R5, c[0x0][0x144]            
/*0098*/                IMAD R8.CC, R3, R5, c[0x0][0x148]             
/*00a0*/                LD.E.64 R6, [R10]                                      
/*00a8*/                IMAD.HI.X R9, R3, R5, c[0x0][0x14c]           
/*00c8*/                ST.E.64 [R8], R6

请注意,现在编译器生成 LD.E.64 和 ST.E.64。 所有其他指令都是相同的。 但是,需要注意的是,执行的指令数量将减少一半,因为循环只执行了 N/2 次。 指令数的这种 2 倍改进在指令绑定或延迟绑定内核中非常重要。

我们也可以写一个vector4版本的copy kernel。

__global__ void device_copy_vector4_kernel(int* d_in, int* d_out, int N) {
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  for(int i = idx; i < N/4; i += blockDim.x * gridDim.x) {
    reinterpret_cast<int4*>(d_out)[i] = reinterpret_cast<int4*>(d_in)[i];
  }

  // in only one thread, process final elements (if there are any)
  int remainder = N%4;
  if (idx==N/4 && remainder!=0) {
    while(remainder) {
      int idx = N - remainder--;
      d_out[idx] = d_in[idx];
    }
  }
}

void device_copy_vector4(int* d_in, int* d_out, int N) {
  int threads = 128;
  int blocks = min((N/4 + threads-1) / threads, MAX_BLOCKS);

  device_copy_vector4_kernel<<<blocks, threads>>>(d_in, d_out, N);
}

对应的SASS如下:

/*0090*/                IMAD R10.CC, R3, R13, c[0x0][0x140]              
/*0098*/                IMAD.HI.X R11, R3, R13, c[0x0][0x144]            
/*00a0*/                IMAD R8.CC, R3, R13, c[0x0][0x148]               
/*00a8*/                LD.E.128 R4, [R10]                               
/*00b0*/                IMAD.HI.X R9, R3, R13, c[0x0][0x14c]             
/*00d0*/                ST.E.128 [R8], R4

这里我们可以看到生成的LD.E.128和ST.E.128。 此版本的代码将指令数减少了 4 倍。您可以在图 2 中看到所有 3 个内核的整体性能。

在这里插入图片描述
Figure 2: Copy bandwidth as a function of copy size for vectorized kernels.

在几乎所有情况下,矢量化负载都优于标量负载。 但是请注意,使用矢量化加载会增加寄存器压力并降低整体并行度。 因此,如果您的内核已经受到寄存器限制或并行度非常低,您可能希望坚持使用标量加载。 此外,如前所述,如果您的指针未对齐或您的数据类型大小(以字节为单位)不是 2 的幂,则您不能使用矢量化加载。

矢量化加载是一种基本的 CUDA 优化,您应该尽可能使用它,因为它们可以增加带宽、减少指令数并减少延迟。 在这篇文章中,我展示了如何通过相对较少的更改轻松地将矢量化负载合并到现有内核中。

参考:

  • https://developer.nvidia.com/blog/cuda-pro-tip-increase-performance-with-vectorized-memory-access/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/35758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#…

Rust权威指南配套手把手笔记

Rust权威指南配套手把手笔记 持续更新ing 共20章&#xff0c;110小节 P1 1.1 - 简介 06:46 P2 1.2 - 安装 Rust 03:18 P3 1.3 - Hello World 04:11 P4 1.4 - Hello Cargo 07:49 P5 2.1 - 猜数游戏&#xff1a;一次猜测 11:47 P6 2.2 - 猜数游戏&#xff1a;生成神秘数字 …

【强化学习论文合集】ICLR-2021 强化学习论文

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

Grad-CAM

其实还是关于yolo的 利用Grad-CAM解释目标检测框架 研究者研究了视觉物体检测器的可解释性问题。具体来说&#xff0c;研究者在YOLO目标检测器的示例中演示了如何将Grad-CAM集成到模型架构中并分析结果。最后展示了如何计算个体检测的基于归因的解释&#xff0c;并发现结果的归…

Biotin-PEG2-alkyne|紫外线可裂解生物素-二聚乙二醇-炔烃|提供光谱图

试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; 紫外线可切割生物素-PEG2-炔烃含有紫外线可切割碎片(containsa UV cleavable Fragemnt)&#xff0c;试剂通过点击化学与含叠氮化物的分子反应。点击化学生物素标记试剂包含各种点…

深入浅出PyTorch——PyTorch可视化

1. 可视化网络结构 在复杂的网络结构中确定每一层的输入结构&#xff0c;方便我们在短时间内完成debug 1.1 使用print函数打印模型基础信息 使用ResNet18的结构进行展示 import torchvision.models as models model models.resnet18() print(model)#打印结果 ResNet((conv1)…

算法学习 | 深度优先搜索~一条道走到黑

目录 员工的重要性 图像渲染 岛屿的周长 被围绕的区域 岛屿数量 深度优先搜索(Depth First Search)&#xff1a;深度优先搜索属于图算法的一种&#xff0c;其过程主要是对每一个可能的分支路径深入到不能再深入到为止&#xff0c;而且每个节点只能访问一次。深度优先搜…

[毕业设计]机器学习的运动目标跟踪-opencv

目录 前言 课题背景和意义 实现技术思路 第一步&#xff1a;创建单目标追踪器 第二步&#xff1a;读取视频的第一帧 第三步&#xff1a;在第一帧中定位物体 第四步&#xff1a;初始化多目标追踪器 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边…

leetcode 907. Sum of Subarray Minimums(子数组最小值的和)

所有子数组的最小值求和。 思路&#xff1a; 最容易想到的就是用DFS找出所有子数组&#xff0c;然后每个子数组找最小值&#xff0c;再求和。但显然不是最优的。 因为费尽心思找到了一堆子数组&#xff0c;它们的最小值竟然是相同的&#xff0c; 是不是有种直接用这个最小值乘…

Alkyne-PEG-Biotin,Alk-PEG-Biotin,炔烃-聚乙二醇-生物素试剂供应

英文&#xff1a;Alkyne-PEG-Biotin&#xff0c;Alk-PEG-Biotin 中文&#xff1a;炔烃-聚乙二醇-生物素 CAS编号&#xff1a;N/A 所属分类&#xff1a;Alkyne PEG Biotin PEG 分子量&#xff1a;可定制&#xff0c;生物素-聚乙二醇5-炔烃、生物素-PEG 20-炔烃 、Biotin-PEG…

HCIA 访问控制列表ACL

一、前言 ACL又称访问控制列表&#xff0c;其实这个东西在很多地方都有用&#xff0c;可能名字不太一样但原理和功能都差不太多&#xff0c;比如服务器、防火墙&#xff0c;都有类似的东西&#xff0c;功能其实也就是“过滤”掉不想收到的数据包。为什么不想收到一些数据包呢&…

C++ 测试框架 Gtest学习——qt版本

目录标题一、参考文档二、获取Gtest三、使用&#xff08;一&#xff09;qt项目导入Gtest&#xff08;二&#xff09;修改pro文件&#xff08;三&#xff09;一个简单的例子&#xff08;四&#xff09;EXPECT&#xff08;期望&#xff09;和ASSERT&#xff08;断言&#xff09;介…

ImportError: cannot import name ‘xxx‘ from ‘xxx‘关于python导包的问题

github clone下来的代码&#xff0c;在矩池云跑的好好的&#xff0c;在自己电脑跑却报错。 ImportError: cannot import name ‘helper’ from ‘utils’ (D:\anaconda\envs\TF2.1\lib\site-packages\utils_init_.py) 搜了网上&#xff0c;说加路径 import sys sys.path.appe…

力控关节机器人(关节扭矩传感器力控)

力控机器人本质上属于协作机器人中的一种&#xff0c;其每个关节都带有力矩传感器&#xff1b; [1] 广泛应用在工业、医疗、新零售领域或智能厨房行业。 Franka Emika&#xff1a; 力控机器人每个关节都带有力矩传感器 力矩传感器提供了一种提高机器人力控性能的途径。 更加…

[毕业设计]基于机器视觉的车辆速度检测与识别算法

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投…

面板平滑转换回归(PSTR)分析案例实现

建模过程包括三个阶段&#xff1a;表述&#xff0c;估计和评估&#xff0c;本文帮助用户进行模型表述、估计&#xff0c;进行PSTR模型评估。 最近我们被客户要求撰写关于PSTR的研究报告&#xff0c;包括一些图形和统计输出。 在程序包中实现了集群依赖性和异方差性一致性检验…

电脑删除的照片怎么找回来?总结了四种方法

照片被删除似乎是常有的事情&#xff0c;如果是重要的照片被删了&#xff0c;想要办法恢复才是最重要的。而对于删除的照片您是如何恢复的呢&#xff1f;这里总结了几种恢复方法&#xff0c;根据自己的需要选择恢复方法&#xff0c;不出意料的话&#xff0c;按照下面的方法你将…

以分割栅格为例实现FME模板的方案优化

一、利用FME分割栅格 &#xff08;一&#xff09;问题的产生 对于FME使用者来说&#xff0c;利用FME完成栅格的批量分割是一件极为平常且容易的事情。只需要输入栅格和确定分割方案就可以实现利用FME对栅格数据的分割&#xff0c;再配合FME的“扇出”功能&#xff0c;就能够实…

WebDAV之葫芦儿·派盘+多彩笔记

多彩笔记 支持webdav方式连接葫芦儿派盘。 还记得小时候那款带密码锁的笔记本?有没有好用的笔记app可以将笔记加上密码,不怕小秘密被偷看?推荐朋友们体验下多彩笔记。 多彩笔记是一款简单又精致的记事软件,用户可以在多彩笔记app中存储,编辑,删除或查找记录,对于写作…

干货 | 如何获取Servlet内存马?

前言 对于回显的获取主要是在ApplicationFilterChain类的lastServicedRequest / lastServicedResponse两个属性&#xff0c;是使用的ThreadLocal进行修饰的&#xff0c;并且&#xff0c;在执行请求的过程中&#xff0c;通过反射修改属性值&#xff0c;能够记录下当前线程的req…