CUDA进阶第二篇:巧用PTX

article/2025/9/13 14:48:50

写在前面

并行线程执行(Parallel Thread eXecution,PTX)代码是编译后的GPU代码的一种中间形式,它可以再次编译为原生的GPU微码。CUDA 手册传送门:Parallel Thread Execution ISA Version 4.3

利用PTX来进行试验,我们可以解决一些在写代码时遇到的不确定问题。下面举几个例子:

  1. 核函数的参数是直接放到寄存器中么?
  2. 一个算法在核函数里面即可以用for来实现也可以用if判断来实现,这两个的执行效率,谁更快一些?
  3. 核函数里面有75个变量,为什么编译的时候显示寄存器用量是60个?
  4. CPU中二进制比乘除法效率高,GPU是否也是这样?

问题1、3可以直接通过看PTX代码得出结论,2、4还需要我们继续做一个验证性试验。下面将给出问题4的解法。

实验中重点分为两部分,第一部分是判断nvcc编译器到底能智能到什么程度(我们都知道编译器在编译的时候会自己做一些优化,比如舍去一些你不用的变量和简化计算等等);如果第一部分的答案是编译器编译乘除法和位移计算得到的ptx指令是相同的(就代表着相当智能了),那么结果很明显就是没有区别,就没有继续的必要。但是如果不一样,那么我们还需要做一个实验判断哪个更快一些了。

1. 根据问题写出测试代码,得到测试代码的PTX代码

首先第一部分,我写了一段简易的测试代码,如下:

__global__ void mul(int *di) 
{ int n = *di; n = n * 2; *di = n; 
} __global__ void div(int *di) 
{ int n = *di; n = n / 2; *di = n; 
} __global__ void shl(int *di) 
{ int n = *di; n = n << 1; *di = n; 
} __global__ void shr(int *di) 
{ int n = *di; n = n >> 1; *di = n; 
}

编译测试代码,编译时加 -keep参数,得到ptx文件内容如下(只摘取重要部分),顺序为乘法、除法、左移、右移:

mul.lo.s32       %r2, %r1, 2;
    ld.global.s32 %r1, [%rd1+0];  // 将参数赋给寄存器 r1shr.s32 %r2, %r1, 31;         // 将r1右移31位,即将符号位移到最低位mov.s32 %r3, 1;               // r3 = 1 and.b32 %r4, %r2, %r3;        // 用 与运算 获取最低位,即获取r1的符号位 add.s32 %r5, %r4, %r1;        // 原数加上符号位赋给r5 shr.s32 %r6, %r5, 1;          // r5 右移一位赋给 r6 st.global.s32 [%rd1+0], %r6;  // 将计算结果重新赋值到 global memory中

这里仔细解释一下除法的PTX代码
经过分析,该计算过程能更加健壮地实现除法运算。对于所有正整数和所有非 -1 负整数,除法运算和右移的结果是没有差别的!但是对于 -1,
-1/2的结果是0(正确结果),-1 >> 1的结果是-1(错误结果)!

shl.b32    %r2, %r1, 1;
shr.s32 %r2, %r1, 1;

根据ptx code 我们可以看出,乘法运算被翻译为mul指令,除法运算被翻译成了5条指令, 位移运算被翻译为shl(左移位)或shr(右移位)指令。故第一部分我们得出的结论为nvcc编译器在编译阶段对乘除法运算和位移运算的编译结果是不同的。

2. 验证试验

因此,进行第二部分实验,验证乘除运算和位移运算哪个速度更快一些。
为了排除其他因素的影响,我选用了NVIDIA_CUDA-5.0_Samples里的vectorAdd(矩阵相加)源代码,对其进行了一些改动,如下:

__global__ void vectorMul(const int *A, int *C, int numElements) 
{ int i = blockDim.x * blockIdx.x + threadIdx.x; if (i < numElements) C[i] = A[i] * 2; 
} __global__ void vectorDiv(const int *A, int *C, int numElements) 
{ int i = blockDim.x * blockIdx.x + threadIdx.x; if (i < numElements) C[i] = A[i] / 2; 
} __global__ void vectorShl(const int *A, int *C, int numElements) 
{ int i = blockDim.x * blockIdx.x + threadIdx.x; if (i < numElements) C[i] = A[i] << 1; 
} __global__ void vectorShr(const int *A, int *C, int numElements) 
{ int i = blockDim.x * blockIdx.x + threadIdx.x; if (i < numElements) C[i] = A[i] >> 1; 
}

测试数据量大小为256,1024,4096, 16384, 65536, 262144, 1048576,进行了10000次循环计算后取速度平均值,结果分别如下:
这里写图片描述

从图中可以看出,除了在数据量较大(>65536)的情况下,除法运算会慢一点(慢不到0.001,大部分的时间都花在了数据传输上),四种运算方式的运行时间是没有绝对的规律。
尽管除法运算会被翻译成较复杂的ptx指令,但GPU的执行速度非常快,所以为了保证代码的可读性,并不建议在核函数中用位移运算代替乘除运算!

写在后面

OpenCUDA:CUDA图像算法开源项目,算法内都有详细的注释,大家一起学习。


私人接各种CUDA相关外包(调试、优化、开发图像算法等),有意向请联系,加好友时请注明。
这里写图片描述


http://chatgpt.dhexx.cn/article/jW8lnRHk.shtml

相关文章

一文了解GPU并行计算CUDA

了解GPU并行计算CUDA 一、CUDA和GPU简介二、GPU工作原理与结构2.1、基础GPU架构2.2、GPU编程模型2.3、软件和硬件的对应关系 三、GPU应用领域四、GPUCPU异构计算五、MPI与CUDA的区别 一、CUDA和GPU简介 CUDA&#xff08;Compute Unified Device Architecture&#xff09;&…

MPI 并行

MPI from b站视频 超算小白-灵犀学院 click &#xff08;一&#xff09;基本框架 头文件 mpi.h #include "mpi.h"初始化函数&#xff1a;MPI_Init( ) MPI_Init(int *argc, char ***argv)完成MPI程序初始化工作&#xff0c;通过获取main函数的参数&#xff0c;让…

C# 并行编程

一 并行任务库TPL 1 并行任务库&#xff08;TPL&#xff0c;Task Parallel Library&#xff09; 2 最重要的是Task类&#xff0c;还有Parallel类 3 Task类&#xff0c;是利用线程池来进行任务的执行 比如直接用ThreadPool更优化&#xff0c;而且编程更方便 4 Paallel类&…

并行处理及分布式系统

并行处理及分布式系统 1 为什么要并行计算 1.1 为什么需要不断提升性能 随着计算能力的增加&#xff0c;我们所面临的计算问题和需求也在增加随着技术的进步&#xff0c;我们从未想过的技术得到了解决&#xff0c;比如&#xff1a;人类基因解码、更准确的医疗成像、更快速准…

并行计算之MPI(一)

MPI学习 1. 了解并行计算 为什么要采用并行计算&#xff1f; &#xff08;1&#xff09;这是因为它可以加快速度即在更短的时间内解决相同的问题或在相同的时间内解决更多更复杂的问题特别是对一些新出现的巨大挑战问题&#xff1b; &#xff08;2&#xff09;节省投入并行计…

并行网关

1、并行网关 假设现在我们想在旁边放一份沙拉。无论如何&#xff0c;如果你想要沙拉&#xff0c;你可以像我们在图1.1中所做的那样建模。 图1.1:准备沙拉和主菜。 在这里&#xff0c;我们介绍了另一个符号&#xff1a;(文本)注释&#xff1b;这是一个您可以与任何流对象(在本…

并行计算:循环程序并行化的一般方法

一、数据划分和处理器指派 1. 带状划分方法 又叫做行列划分&#xff0c;就是将矩阵的整行或整列分成若干组&#xff0c;各组指派给一个处理器。 例如&#xff1a;设矩阵A由n行和m列&#xff0c;对其串行处理的程序段如下&#xff1a; for i1 to n dofor j1 to m doProcess(a[…

并行计算之MPI(三)

了解MPI 什么是MPI &#xff08;1&#xff09;MPI是一个库而不是一门语言&#xff0c;许多人认为MPI就是一种并行语言&#xff0c;这是不准确的。但是按照并行语言的分类可以把FORTRANMPI或CMPI。看作是一种在原来串行语言基础之上扩展后得到的并行语言&#xff0c;MPI库可以被…

并行计算的一些思考与总结

弗林分类法 根据弗林分类法&#xff0c;计算机结构主要分为 SIMD----单指令、多数据MIMD---多指令、多数据SISD----单指令、单数据MISD---多指令、单数据 一般的串行程序中为SISD&#xff0c;即在单核CPU下任何时间和地点只有一个指令处理一个数据&#xff0c;其所谓的多线程…

并行计算之MPI(二)

1.并行编程模型 目前两种最重要的并行编程模型是数据并行和消息传递数据并行编程模型的编程级别比较高编程相对简单但它仅适用于数据并行问题消息传递编程模型的编程级别相对较低但消息传递编程模型可以有更广泛的应用范围。 数据并行即将相同的操作同时作用于不同的数据因此…

Matlab 并行

Matlab 并行 1. 检查是否有并行附加功能2. 创建和删除并行2.1 创建默认的并行池2.2 在本地创建2.3 在集群创建2.4 删除 3. Parallel pool 包含的一些函数3.1 parfor3.2 parfeval 初学&#xff0c;肯定有理解不够的地方。看官方文件更靠谱。 1. 检查是否有并行附加功能 如果没有…

并行处理及分布式系统期末总结笔记

并行处理及分布式系统期末总结笔记 1、任务并行、数据并行的应用2、冯诺依曼体系结构的瓶颈及改进&#xff0c;Flynn分类法涉及的几种模型及其特点3、Cache的特点&#xff0c;Cache缺失、Cache命中、Cache一致性及解决方法、伪共享、流水线、多发射4、加速比、效率、阿姆达尔定…

并行程序设计导论期末复习

任务并行、数据并行的应用 任务并行 将待解决问题所需要执行的各个任务分配到各个核上执行。 数据并行 将待解决问题所需要处理的数据分配给各个核&#xff0c;每个核在分配到的数据集上执行大致相似的操作。 冯诺依曼体系结构的瓶颈及改进&#xff0c;Flynn分类法涉及的几…

并行程序设计导论 概念总结

Parallel Programing caiyi 2021/6/17 第一章 1.为什么要构建并行系统? 电路晶体管密度过大会使处理器能耗增加&#xff0c;散热的问题使通过继续增快集成电路密度提高处理器性能不再现实&#xff0c;因此集成电路商决定构建多核处理器。 2.为什么要编写并行程序&#xf…

cuda 并行计算

1 简介 2006年&#xff0c;NVIDIA公司发布了CUDA&#xff0c;CUDA是建立在NVIDIA的GPU上的一个通用并行计算平台和编程模型&#xff0c;基于CUDA编程可以利用GPU的并行计算引擎来更加高效地解决比较复杂的计算难题。CUDA是NVIDIA公司所开发的GPU编程模型&#xff0c;它提供了GP…

数据 并行

first 含义是计算机内包含一组处理单元&#xff08;PE&#xff09;&#xff0c;每一个处理单元存储一个&#xff08;或多个&#xff09;数据元素。当机器执行顺序程序时&#xff0c;可对应于全部或部分的内部处理单元所存的数据同时操作。 将并行处理技术引入信息检索领域 把数…

并行的常见问题和注意事项

关于Oracle中的并行&#xff0c;可以说是一把双刃剑&#xff0c;用得好&#xff0c;可以充分利用系统资源&#xff0c;提升数据库的处理能力&#xff0c;用得不好&#xff0c;可能会适得其反。 并行的基本使用方法&#xff0c;对于大部分SQL开发者和DBA来说&#xff0c;并行的一…

并发和串行、并行的概念

先抛开语言不管&#xff0c;只聊概念&#xff0c;说起并发&#xff0c;就很容易想到它和串行、并行的区别。 串行&#xff1a;一次只能取得一个任务并执行这个任务&#xff0c;这个任务执行完后面的任务才能继续&#xff1b; 并发&#xff1a;指的是在同一个时间段内&#xf…

牛腩新闻发布--过程或函数 'news_selectByCaId' 需要参数 '@caid',但未提供该参数(二)

发现问题 之前有一篇博客是因为存储过程中没有添加相应的函数&#xff0c;导致出现了“过程或函数 ‘news_selectByCaId’ 需要参数 ‘caid’&#xff0c;但未提供该参数”&#xff0c;这次继续出现了这样一个问题&#xff0c;但是出现的错误就不再过程函数中了&#xff0c;而…

牛腩新闻发布--过程或函数 'news_selectByCaId' 需要参数 '@caid',但未提供该参数(三)

发现问题 这篇博客是建立在“牛腩新闻发布–过程或函数 ‘news_selectByCaId’ 需要参数 ‘caid’&#xff0c;但未提供该参数&#xff08;二&#xff09;”&#xff0c;因为在那篇博客中说出了我当时遇到的“过程或函数 ‘news_selectByCaId’ 需要参数 ‘caid’&#xff0c;…