CUDA ---- Kernel性能调节
Exposing Parallelism
这部分主要介绍并行分析,涉及掌握nvprof的几个metric参数,具体的这些调节为什么会影响性能会在后续博文解释。
代码准备
下面是我们的kernel函数sumMatrixOnGPUD:
__global__ void sumMatrixOnGPU2D(float *A, float *B, float *C, int NX, int NY) {
unsigned int ix = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int iy = blockIdx.y * blockDim.y + threadIdx.y;
unsigned int idx = iy * NX + ix;
if (ix < NX && iy < NY) {
C[idx] = A[idx] + B[idx];
}
}
我们指定一个比较大的数据矩阵,包含16384个元素:
int nx = <<;
int ny = <<;
下面的代码用来配置main函数的参数,也就是block的维度配置:
if (argc > ) {
dimx = atoi(argv[]);
dimy = atoi(argv[]);
}
dim3 block(dimx, dimy);
dim3 grid((nx + block.x - ) / block.x, (ny + block.y - ) / block.y);
编译:
$ nvcc -O3 -arch=sm_20 sumMatrix.cu -o sumMatrix
Checking Active Warps with nvprof
在做各项数据比较的时候需要有个基准,这里使用四个block配置的时间消耗作为基准观察,分别为(32,32)(32,16)(16,32)和(16,16),本文开始时有提到,第一个参数是x象限维度,第二个参数是y象限维度。
下面是几种配置的时间消耗输出结果:
$ ./sumMatrix
sumMatrixOnGPU2D <<< (,), (,) >>> elapsed ms
$ ./sumMatrix
sumMatrixOnGPU2D <<< (,), (,) >>> elapsed ms
$ ./sumMatrix
sumMatrixOnGPU2D <<< (,), (,) >>> elapsed ms
$ ./sumMatrix
sumMatrixOnGPU2D <<< (,),(,) >>> elapsed ms
比较这几个结果,不难发现,最慢的是第一个(32,32),最快的是第二个(32,16),这里可以猜测的到的是,拥有更多的block并行性更好。这个猜测可以使用nvprof 的achieved_occupancy这个metric参数来验证。该参数的定义公式在上一篇博文有介绍,实际上就是指每个SM在每个cycle能够达到的最大active warp数目占总warp的比例。下面是使用该参数后得到的结果:
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.501071
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.736900
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.766037
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,),(,)>>> Achieved Occupancy 0.810691
从上面的输出可以得知两件事儿:
- 由于第二个配置比第一个有更多的block,device就会达到更多active warp(跟鸡蛋放在多个篮子的道理差不多)。也就是第二个性能优于第一个的原因。
- 第四个的achieved Occupancy最高,但是却不是最快的,因此,较高的achieved Occupancy并不一定就意味着更好的性能,也就是说还有更多的因素影响着GPU的性能。
checking memory operations with nvprof
对于C[idx] = A[idx] + B[idx]来说共有三个memory操作:两个memory load和一个memory store。要查看这些操作的效率可以使用nvprof的两个metric参数,如果想要查看memory的throughput,则可使用gld_throughput:
$ nvprof --metrics gld_throughput./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Load Throughput .908GB/s
$ nvprof --metrics gld_throughput./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Load Throughput .478GB/s
$ nvprof --metrics gld_throughput./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Load Throughput .195GB/s
$ nvprof --metrics gld_throughput./sumMatrix
sumMatrixOnGPU2D <<<(,),(,)>>> Global Load Throughput .708GB/s
不难看到,第四个拥有最高的load throughput,但是却比第二个慢(第二个也就是第四个的一半),所以,较高的load throughput也不一定就有较高的性能。之后讲到memory transaction时会具体分析这种现象的原因,简单说,就是高load throughput有可能是一种假象,如果需要的数据在memory中存储格式未对齐不连续,会导致许多额外的不必要的load操作,所以本文中的efficiency会这么低。
然后,我们可以使用nvprof的gld_efficiency来度量load efficiency,该metric参数是指我们确切需要的global load throughput与实际得到global load memory的比值。这个metric参数可以让我们知道,APP的load操作利用device memory bandwidth的程度:
$ nvprof --metrics gld_efficiency ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Memory Load Efficiency 100.00%
$ nvprof --metrics gld_efficiency ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Memory Load Efficiency 100.00%
$ nvprof --metrics gld_efficiency ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Global Memory Load Efficiency 49.96%
$ nvprof --metrics gld_efficiency ./sumMatrix
sumMatrixOnGPU2D <<<(,),(,)>>> Global Memory Load Efficiency 49.80%
从上述结果可知,最后两个的load efficiency只是前两个的一半。这也可以解释,为什么较高的throughput和较高的Occupancy却没有产生较好的性能。尽管最后两个的load操作数目要多不少(因为二者throughput较高),但是他们的load effecitiveness却低不少(由于efficiency较低)。
观察最后两个可以发现,他们block的x象限配置是warp的一半,由前文推测知,该象限应该保持为warp大小的整数倍。关于其具体原因将在后续博文详细解释。
Exposing More Parallelism
我们现在可以得出一个结论就是blockDim.x应该是warp大小的整数倍。这样做是很容易就提升了load efficiency。现在,我们可能还有其他疑惑,比如:
- 继续调整blockDim.x是否会继续增加load throughput?
- 还有其他方法能增大并行性吗?
现在,我们重新整一个基准数据出来,这两个问题可以从这个基准分析个大概:
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> elapsed 0.033567 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> elapsed 0.034908 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> elapsed 0.036651 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.032688 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.034786 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.046157 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.032793 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.038092 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.000173 sec
Error: sumMatrix.cu:, code:, reason: invalid configuration argument
从上面数据,我们能够分析出来的是:
- 最后一个配置(256,8)不可行,block中总共的thread数目超过了1024,这是GPU的硬件限制。
- 最好的结果是第四个(128,2)。
- 第一个启动了最多的block,但不是最快的。
- 因为第二个与第四个在一个block中拥有相同数目的thread,本应猜测二者有相同的表现,但是实际却是第二个略逊色,所以blockDim.x的大小是很关键的。
- 剩下的相对第四个都有较少的block数目,所以并行规模也是影响性能的关键因素。
现在,我们又得猜测了,拥有block最少的应该会有一个最低的achieved Occupancy吧?而拥有最多block的应该会达到最高的achieved Occupancy吧?为了验证这些想法,我们再看一组数据:
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> Achieved Occupancy 0.554556
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> Achieved Occupancy 0.798622
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> Achieved Occupancy 0.753532
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.802598
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.746367
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> Achieved Occupancy 0.573449
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> Achieved Occupancy 0.760901
$ nvprof --metrics achieved_occupancy ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,) >>> Achieved Occupancy 0.595197
看到了吧,(64,2)的achieved Occupancy竟然是最低的,尽管他有最多的block(高中做物理题也是这感觉),它达到了硬件对block数量的限制。
第四个(128,2)和第七个(256,2)拥有拥有差不多的achieved Occupancy。我们对这两个再做一个试验,再次增大,将blockDim.y设置为1,这也减少了block的大小:
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,),(,)>>> elapsed 0.032602 sec
$ ./sumMatrix
sumMatrixOnGPU2D <<<(,), (,)>>> elapsed 0.030959 sec
这次配置产生了最佳的性能,特别是,(256,1)要比(128,1)要更好,,再次检查achieved Occupancy,load throughput和load efficiency:
$ nvprof --metrics achieved_occupancy ./sumMatrix
$ nvprof --metrics gld_throughput ./sumMatrix
$ nvprof --metrics gld_efficiency ./sumMatrix
输出:
Achieved Occupancy 0.808622
Global Load Throughput .762GB/s
Global Memory Load Efficiency 100.00%
现在可以看出,最佳配置既不是拥有最高achieved Occupancy也不是最高load throughput的。所以不存在唯一metric来优化计算性能,我么需要从众多metric中寻求一个平衡。
总结
- 在大多数情形下,并不存在唯一的metric可以精确的优化性能。
- 哪个metric或者event对性能的影响大是由kernel具体的代码决定的。
- 在众多相关的metric和event中寻求一个平衡。
- Grid/blcok heuristics(启发) 为调节性能提供了不错的切入点。
CUDA ---- Kernel性能调节的更多相关文章
- Pitfalls of using opencv GpuMat data in CUDA kernel code
Please note that cv::cuda::GpuMat and cv::Mat using different memory allocation method. cv::cuda::Gp ...
- OpenCL 增强单work-item kernel性能策略
1.基于反馈的Optimization Report解决单个Work-item的Kernel相关性 在许多情况下,将OpenCL™应用程序设计为单个工作项内核就足以在不执行其他优化步骤的情况下最大化性 ...
- 60 cuda全局性能优化
0 引言 cuda线程模型涉及grid的块划分和线程配置,直接影响到全局运算速度.根据文档<CUDA_C_Programming_Guide>,性能优化有三个方面的基本策略. (1)最大化 ...
- yii性能调节
网络应用程序的性能受很多因素的影响.数据库存取,文件系统操作,网络带宽等都是潜在的影响因素. Yii 已在各个方面减少框架带来的性能影响.但是在用户的应用中仍有很多地方可以被改善来提高性能. 1. 开 ...
- oracle修改内存使用和性能调节,SGA
最近装了oracle,电脑实在太卡了,想要限制内存使用,结果碰到一系列问题: 要用SYS帐户登录,修改SGA使用,结果不知道SYS密码.用SYSTEM帐户权限不够. 试了几条语句后,有几个文件修改不了 ...
- 4.2 CUDA Reduction 一步一步优化
Reduction并行分析: 每个线程是基于一个树状的访问模型,从上至下,上一层读取数据相加得到下一层的数据.不停的迭代,直到访问完所有的数据. 利用这么多的线程块(thread block)我们需要 ...
- CUDA C
一.CUDA结构 硬件:GPU(Graphics Processing Unit) SM(Streaming Multiprocessor) SP(Streaming Processor) ...
- MySQL性能分析(转)
第一步:检查系统的状态 通过操作系统的一些工具检查系统的状态,比如CPU.内存.交换.磁盘的利用率.IO.网络,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲,这也可能不是一个正常的状 ...
- CUDA2.4-原理之性能优化及浮点运算
本部分来自于<大规模并行处理器编程实战>第六章.第七章.打算不再看这本书了,准备看<programming massively parallel processors 2nd> ...
随机推荐
- poj 2449 Remmarguts' Date 求第k短路 Astar算法
=.=好菜 #include <iostream> #include <cstdio> #include <string.h> #include <cstri ...
- AtCoder 杂题训练
前言: 因为要普及了,今年没一等就可以退役去学文化课了,所以暑假把历年noip普及组都刷了一遍,离noip还有50+天,想弄点强化训练什么的. 想了想,就这些天学文化课之余有空就把AtCoder之前那 ...
- ASP.NET MVC之Bundle压缩JS和CSS
介绍Bundle之前先引用<淘宝技术这十年>中一段话,对Web前端稍微有点常识的人都应该知道,浏览器下一步会加载页面中用到的CSS.JS(JavaScript).图片等样式.脚本和资源文件 ...
- spring实现定时任务的两种方式
本文为博主原创,未经允许不得转载 项目中要经常事项定时功能,在网上学习了下用spring的定时功能,基本有两种方式,在这里进行简单的总结, 以供后续参考,此篇只做简单的应用. 1.在spring-se ...
- POJ 1029 False coin
http://poj.org/problem?id=1029 题意: 在一堆硬币中有一个假硬币,重量是重是轻不知道.每次称量多个硬币,并给出称量结果.判断依据题目给出的几次称量结果能否找出假硬币. 思 ...
- 【Python】【有趣的模块】【Bobo】
[python web框架之 bobo的安装配置] [Mac] 我的Mac环境,python3.5 1. 安装bobo : >>> pip3 install bobo 2. 配 ...
- ubuntu 14.04(desktop amd 64) nginx 安装启动停止
sudo apt-get install nginx 关闭: sudo service nginx stop 启动: sudo nginx
- Django模板语言详解
本节将介绍Django模版系统的语法.Django模版语言致力于在性能和简单性上取得平衡. 如果你有过其它编程背景,或者使用过一些在HTML中直接混入程序代码的语言,那么你需要记住,Django的模版 ...
- django路由转发
一.路由转发 通常,我们会在每个app里,各自创建一个urls.py路由模块,然后从根路由出发,将app所属的url请求,全部转发到相应的urls.py模块中. 例如,下面是Django网站本身的UR ...
- Beta 冲刺 第三天
第三天 2018.6.26 今日完成任务情况. 妥志福.牛瑞鑫: 完成任务:让用户接触系统,指出系统中存在的问题,并统计修改的问题,提出修改的方案. 王胜海.马中林: 完成任务:对文档中存在的问题修改 ...