__global__ void add( int *a, int *b, int *c) {
<span style="white-space:pre"> </span> int tid = threadIdx.x + blockIdx.x *blockDim.x;
<span style="white-space:pre"> </span>while (tid < N) {
<span style="white-space:pre"> </span> c[tid] = a[tid] + b[tid];//your operation
<span style="white-space:pre"> </span> tid += blockDim.x * gridDim.x;
}
}

来自《GPU高性能编程CUDA实战》之5.2.1节,这里是采用一维数组(也就是向量)例子,而不是二维矩阵的形式。

对于任何一个显卡来说,都有硬件的限制,以前的显卡在线程块的数量上有65535的上限,而且每个线程块中的线程数量也有限制,具体的数值可以通过设备属性结构中的maxThreadsPerBlock域的值来查找,该值可以通过函数cudaGetDeviceProperties来得到:

cudaDeviceProp prop;//声明一个结构体来接收显卡属性信息

cudaGetDeviceProperties( &prop, 0 );//调用函数将第0块显卡属性信息提取出来

printf( "Max threads per block:%d\n",prop.maxThreadsPerBlock );//显示每个块上线程数量的上限

这里介绍两个新的内置变量 blockDim和gridDim(这两个变量都有.x;.y;.z三个域表示三个不同维度);前者对于所有的线程块来说,都是一个常数,表示一个线程块中每一维的线程数量;后者对于所有的网格来说,都是一个常数,表示一个网格中每一维的线程块的数量,不过通常来说是二维的网格,所以.z一般都是1。

可以通过对核函数的调用来进行分配线程,一个较为简单的方法是:

add<<<(N+127)/128,128>>>(dev_a,dev_b,dev_c);

将每个块设置成128(举例而已)个线程数量,而N表示你的矩阵的所需总的线程数,当整除的时候刚刚好,不能整除的时候,会多开一个线程块,并通过下面的方法来进行限制:

If(tid<N)//tid表示当前是第几个线程数

/*your operation*/;

换句话说就是当执行的线程数量超过总的所需的线程数量的时候不会有任何的操作。

不过因为网格中的线程块有限制,而线程块中的线程有限制,也就是说当所需要线程的个数超过65535×128=8388460的时候,核函数会调用失败,所以还是需要更大限度的上限。可以用如下的方法:

上面就是核函数的原理代码,意图通过将并行化过程与硬件的实际执行过程解耦开来(就是写代码的时候可以不需要考虑硬件有几个流处理器,也就是之前说的透明性和程序的可扩展性),减轻CUDA程序员的负担。上面就是两个索引的迭代作为核心原理部分:

int tid = threadIdx.x + blockIdx.x *blockDim.x;

通过kernel函数的线程索引,这里以块的索引号乘以块的x上的维度的大小(这个块中的线程数量,因为这里是以一维向量做例子,所以只有x,没有y),那么就是跳过了前面的块,然后加上当前块中的线程索引,那么tid就是所需要计算的线程的索引了;这里使用while(){}的方法是使用分网格的方式,不过网格本就是分开的,因为kernel的<<<>>>两个参数中,第一个是网格的块的维度gridDim,第二个是块的线程维度blockDim,所以没有网格的维度,也就是说上面的tid是第一个网格的线程索引,然后通过加上while(){}中的代码:tid
+= blockDim.x * gridDim.x;来将当前网格维度的线程索引以网格维度乘以网格的块的维度(就是每个线程块中的线程数量乘以网格中线程块的数量)来进行递增,就是跳到另一个网格上去(当然了,这是逻辑上的,不是物理上的),既然这样能够无视网格中的块的限制,那么就可以将kernel的参数进行自定义的限制了:add<<<128,128>>>(dev_a,dev_b,dev_c);(128是自定义的,如果可以最好按照上一节中的存储器 和warp的要求来设置)那么这时候这个一维数组的长度只取决于GPU上存储器的容量了,可以通过下面的方法来验证是否成功了:

// verify that the GPU did the work werequested
bool success = true;/设置标识
for (int i=0; i<N; i++) {
if ((a[i] + b[i]) != c[i]) {
printf( “Error: %d + %d != %d\n”, a[i], b[i], c[i] );//这里的操作是加操作
success = false;//如果没成功那么将这个标识设置成失败
}
}
if (success) printf( "We did it!\n" );

用事件来测量性能

在cpu上可以使用cpu或者操作系统的计时器来计算一个任务的执行时间,而先不说这有各种延迟(os的线程调度或者高精度cpu计时器的可用性),而且gpu核函数运行时,主机还是可以异步执行计算,所以没法用以往的方式来测量gpu上某个任务上花费的时间,所以需要使用CUDA的事件API来解决这个问题。

其本质是一个GPU时间戳,只需要两个步骤:创建一个事件,然后记录这个事件:

cudaEvent_t start, stop;

cudaEventCreate(&start);

cudaEventCreate(&stop);

cudaEventRecord( start, 0 );

// do some work on the GPU

cudaEventRecord( stop, 0 );

先创建开始和终止的事件,然后调用记录事件函数(这里的第二个参数0 暂时先不说,记住就行,后续在介绍)记录开始的事件,然后进行操作,接着调用记录事件函数记录终止的事件。

但是因为cpu和gpu可以进行异步的函数调用,也就是说当gpu开始执行代码,但是未执行完成的时候,cpu却能够继续执行程序中的下一行代码,从性能的角度看是非常不错的,但是从逻辑角度来看,这样就使得计时工作没法准确的进行下去(中间虽然说是填写do work on gpu的代码,但是这部分难免会有在主机上的代码,比如先在主机上分配内存和数据然后在复制到设备上)。所以还需要将它们进行同步起来,所以需要在后面加上一个函数:cudaEventSynchronize( stop );

,最后的结果如下:

cudaEvent_t start, stop;

cudaEventCreate(&start);

cudaEventCreate(&stop);

cudaEventRecord( start, 0 );

// do some work on the GPU

cudaEventRecord( stop, 0 );

cudaEventSynchronize( stop );

float elapsedTime;

cudaEventElapsedTime( &elapsedTime,start, stop ) ;

printf( "Time to generate: %3.1f ms\n", elapsedTime );

cudaEventDestroy( start );

cudaEventDestroy( stop ) ;

这样会告诉运行时系统阻塞后面的代码,而且当cudaEventSynchronize( stop );函数返回的时候,就知道在stop事件之前的所有gpu工作都已经完成了,可以安全的读取在stop中保持的时间戳,不过记得,因为cuda事件是直接在gpu上实现的,所以不适合用于同时包含设备代码和主机代码的混合代码计时,也就是说,不能依靠这个来通过cuda事件对核函数和设备内存复制之外的代码进行计时,因为结果不准(个人:这个函数的意思应该是让cpu和gpu同步起来,使得这个函数前面的代码都能够执行完成,而且cpu不会跳到后面去执行,但是因为cpu中会有延时或者什么,因为毕竟这里的事件是在gpu上执行的,所以估计这才是不准确的原因吧,不过关于这个函数的更详细的具体意义可以查看cuda自带的文档)。这里的cudaEventElapsedTime()和cudaEventDestory()是两个收尾的函数,前者是一个工具函数用来计算两个事件之间经历的时间,第一个参数是某个浮点变量的地址,其中记录着两个事件之间的时间,单位为毫秒;后个函数就是简单的释放掉事件的函数

CUDA2.3-原理之任意长度的矢量求和与用事件来测量性能的更多相关文章

  1. gpu对任意长度的矢量求和

    blockDim.x*gridDim.x 跳过一个grid int <<<参数1,参数2>>>(int *a,int * b,int * c); 如果是一维的,参数 ...

  2. [c++]大数运算1---利用C++ string实现任意长度正小数、整数之间的加减法

    一.概述 本文属于大大维原创,未经笔者本人允许,严禁转载!!! C/C++中的int类型能表示的范围是-2E31-2E31–1.unsigned类型能表示的范围是0-2E32–1,即 0-429496 ...

  3. [c++]大数运算---利用C++ string实现任意长度正小数、整数之间的加减法

    本文为大大维原创,最早于博客园发表,转载请注明出处!!! 一.概述 C/C++中的int类型能表示的范围是-2E31-2E31–1.unsigned类型能表示的范围是0-2E32–1,即 0-4294 ...

  4. 生成任意长度的随机数 JS

    1.Math.random().toString(36).substr(2); 结果:ywv6cnpkliahj4tep0 2.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...

  5. 求任意长度数组的最大值(整数类型)。利用params参数实现任意长度的改变。

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. C语言---递归反向输出任意长度的字符串

    (该字符串可以包含空格和回车!) [题目要求] 编写一个递归函数,实现将输入的任意长度的字符串反向输出的功能. 例如输入字符串:ABCD,输出字符串:DCBA. [题目分析] 应用递归的思想有时可以很 ...

  7. 任意长度的正小数的加法(YT新人之巅峰大决战05)

    Problem Description 话说,经过了漫长的一个多月,小明已经成长了许多,所以他改了一个名字叫"大明". 这时他已经不是那个只会做100以内加法的那个"小明 ...

  8. 【模板小程序】任意长度非负十进制数转化为二进制(java实现)

    妈妈再也不用担心十进制数过大了233(注意只支持非负数) import com.google.common.base.Strings; import java.math.BigInteger; imp ...

  9. [POJ 2821]TN's Kindom III(任意长度循环卷积的Bluestein算法)

    [POJ 2821]TN's Kindom III(任意长度循环卷积的Bluestein算法) 题面 给出两个长度为\(n\)的序列\(B,C\),已知\(A\)和\(B\)的循环卷积为\(C\),求 ...

随机推荐

  1. 十五天精通WCF——第三天 client如何知道server提供的功能清单

     通常我们去大保健的时候,都会找姑娘问一下这里能提供什么服务,什么价格,这时候可能姑娘会跟你口述一些服务或者提供一份服务清单,这样的话大 家就可以做到童嫂无欺,这样一份活生生的例子,在wcf中同样是一 ...

  2. Jetty 发布web服务

    Jetty provides a Web server and javax.servlet container, plus support for HTTP/2, WebSocket, OSGi, J ...

  3. Maven详细介绍

    Maven 目录 1 什么是Maven? 2 Maven 的好处 3 获取和安装 3.1 获取 3.2 安装 3.2.1 环境变量的配置 4 设置本地仓库 5 创建简单的Maven实例 5.1 使用骨 ...

  4. ASP.NET Core 1.0 安装并发布到Centos 7.2 使用jexus 5.8.2

    安装运行环境 sudoyuminstall libunwind libicu 下载.net core https://www.microsoft.com/net/download 下载完后上传文件 安 ...

  5. ELF Format 笔记(一)—— 概述

    ilocker:关注 Android 安全(新手) QQ: 2597294287 ELF Object files 参与程序的链接和执行,从这两个角度分别有两种视图: ELF header 位于文件的 ...

  6. 二:Go编程语言规范-类型

    1.类型 布尔值,数值与字符串类型的实例的命名是预声明的. 数组,结构,指针,函数,接口,切片,映射和信道这些复合类型可由类型字面构造. 每个类型 T 都有一个 基本类型:若 T 为预声明类型或类型字 ...

  7. Windows 10 Threshold 2 升级记录

    昨天(11月17日)升级到Windows 10 Threshold 2版本.我的使用的设备是Surface Pro 3,4G内存,128G硬盘. Threshold 2是作为一个Windows系统更新 ...

  8. 【HTML5】使用多媒体

    HTML5 支持直接在浏览器中播放音频和视频文件,不需要使用Abode Flash这样的插件. 1. 使用 video 元素 可以用video 元素在网页里嵌入视频内容. 其基本用法如下: <! ...

  9. UVA 12169 Disgruntled Judge【扩展欧几里德】

    题意:随机选取x1,a,b,根据公式xi=(a*xi-1+b)%10001得到一个长度为2*n的序列,奇数项作为输入,求偶数项,若有多种,随机输出一组答案. 思路:a和b均未知,可以考虑枚举a和b,时 ...

  10. VIJOS1476旅游规划[树形DP 树的直径]

    描述 W市的交通规划出现了重大问题,市政府下决心在全市的各大交通路口安排交通疏导员来疏导密集的车流.但由于人员不足,W市市长决定只在最需要安排人员的路口安放人员.具体说来,W市的交通网络十分简单,它包 ...