这部分来自于《CUDA_C_Programming_Guide.pdf》,看完《GPU高性能变成CUDA实战》的第四章,觉得这本书还是很好的,是一种循序渐进式的书,值得看,而不是工具书那种,适合入门,看完这章,觉得应该先简单的列下函数类型限定符,顺带列下变量类型限定符。知识是“积少成多”的。

ps;极力推荐使用编辑器之神-vim来写代码,正打算没事一点一点的使用这个神器,抛却其他编辑器,每天不需要学新东西,如果能够使用超过半年,我想有了熟悉感,学习其他的就不难了,这也是我极力倡导的:软件、算法、代码、数学等知识,不是一蹴而就的,可以每天稍微接触点,时间长了除却了陌生感,再次拾起的时候也会上手很快(那些有时间这么干的一群人,比如学生。切记贪多嚼不烂,主打几个方面,以1-3个方面为主,其他为辅,可是为辅不代表等用的时候再去接触,那时候肯定手忙脚乱一团糊,为辅可以不精通,可是最好需要了解,入门)。

一、函数类型限定符

函数类型限定符是用来指定这个函数的运行极其调用是在host(主机)还是device(设备)上运行的。主机:cpu及内存条;设备:gpu及显存。

1、__device__

这个限定符声明的函数:a、在设备上运行;b、只能被设备调用;

2、__global__

这个限定符声明的函数是可以作为核函数看待,该函数:a、在设备上运行;b、可以从主机上被调用;c、可以被计算能力为3.x的设备调用。

声明的函数的返回值必须是void类型。

任何调用这种函数的函数必须指定它的运行配置(在执行配置部分介绍,这里略)。

这种函数是异步的,也就是在设备完全完成它的运行之前就返回了。

3、__host__

这个限定符声明的函数:a、在主机上运行;b、只能被主机调用;

当函数前面没有任何函数类型限定符的时候,默认的就是这个限定符。

__global__和__host__不能一起声明同一个函数。

__device__和__host__可以一起声明同一个函数,这是为了让编译器编译出两个不同的版本,在Application Compatibility中的红__CUDA_ARCH__可以用来区分代码的路径是来自于主机还是设备:

  1. __host__ __device__ func()
  2. {
  3. #if __CUDA_ARCH__ >= 300
  4. // Device code path for compute capability 3.x
  5. #elif __CUDA_ARCH__ >= 200
  6. // Device code path for compute capability 2.x
  7. #elif __CUDA_ARCH__ >= 100
  8. // Device code path for compute capability 1.x
  9. #elif !defined(__CUDA_ARCH__)
  10. // Host code path
  11. #endif

4、__noinline__ 和__forceinline__

当为计算能力为1.x的设备编译代码的时候,__device__函数默认是内联的。当为计算能力为2.x或者更高的设备编译代码的时候,__device__函数只有当编译器认为适当的时候才会是内联的。

__noinline__函数限定符可以用来作为编译器不将函数作为内联函数处理的标识。函数体必须在与被调用的文件中,(即调用的函数和函数的定义要在同一个文件中)。对于计算能力为1.x的设备来说,即使使用__noinline__函数限定符,编译器也不会执行带有指针参数和带有很长的参数列表的函数的。

__forceinline__函数限定符可以用来强制编译器将函数作为内联函数看待。

二、变量类型限定符

变量类型限定符用来指定变量在设备上的存储位置(因为主机上不需要这个限定符)。

在设备代码中没有任何__device__、__shared__、 __constant__定符的自动变量声明通常是存储在一个寄存器上的。但是在某些情况下,编译器会选择将它存放在局部存储器(会有不良的执行结果)上。

1、__device__

用来声明变量,使其存储在设备上。

大部分情况来说,下面的两个变量限定符中任何的一个通常都会与__device__一起使用,用来指定存储变量的空间,如果没有那两个变量限定符一起使用的话,这个变量:

存储在全局存储空间中;

有着与应用程序一样长的生命周期;

可以被grid中所有的线程和从运行时库中的主机函数所访问。其中的函数例如:cudaGetSymbolAddress() / cudaGetSymbolSize() /cudaMemcpyToSymbol() / cudaMemcpyFromSymbol().

可以被额外的__managed__限定符所限定,这样的一个变量可以直接被主机代码所引用,例如:它的地址可以直接在一个主机函数中被执行、被读、被写。为了方便 __managed__就表示 __managed__   __device__,当使用__managed__限定符的时候,__device__限定符可以省略。

2、__constant__

这个限定符是作为与__device__限定符可选的额外限定符,它表示的变量:

存储在常量存储空间中;

有着与应用程序一样长的生命周期;

可以被grid中所有的线程和来自运行时库中的主机函数所访问。其中的函数例如:cudaGetSymbolAddress() / cudaGetSymbolSize() /cudaMemcpyToSymbol() / cudaMemcpyFromSymbol().

3、__shared__

这个限定符也是作为与__device__限定符可选的额外限定符,它声明的变量:

存储在一个线程块的共享存储器中

有着与这个块一样的生命周期;

只能被这个块中的所有线程所访问。

当在共享存储器中声明一个变量作为外部数组,例如:

extern __shared__ float shared[];

这个数组的大小是在运行的时候决定的。所有以这种形式定义的变量,开始与存储器中相同的地址,所以数组中变量的布局必须是通过offsets所显式管理的。例如,如果你想在动态分配共享存储器中得到如下的等价情况:

short array0[128];

float array1[64];

int array2[256];

,你应该按照下面的方式来声明和初始化这些数组:

  1. extern __shared__ float array[];
  2. __device__ void func() // __device__ or __global__ function
  3. {
  4. short* array0 = (short*)array;
  5. float* array1 = (float*)&array0[128];
  6. int* array2 = (int*)&array1[64];
  7. }

注意指针需要预先分配为所指定类型的大小,所以下面的代码不会起效果,因为array1没有被指派成4个字节:

  1. extern __shared__ float array[];
  2. __device__ void func() // __device__ or __global__ function
  3. {
  4. short* array0 = (short*)array;
  5. float* array1 = (float*)&array0[127];
  6. }

4、__restrict__

nvcc支持通过__restrict__关键字来指定受限指针。

在C99标准中引入的受限指针可以缓解存在于C类型语言中的别名问题,这个问题就是:禁止所有来自代码重排序到公共子表达消除中的所有优化。

下面就是一个别名问题,如果使用受限指针,那么就有助于编译器减少指令的数量:

  1. void foo(const float* a,
  2. const float* b,
  3. float* c)
  4. {
  5. c[0] = a[0] * b[0];
  6. c[1] = a[0] * b[0];
  7. c[2] = a[0] * b[0] * a[1];
  8. c[3] = a[0] * a[1];
  9. c[4] = a[0] * b[0];
  10. c[5] = b[0];
  11. ...
  12. }

在C类型语言中,指针a,b,c也许会出现别名,所以任何通过c 的写操作会修改a 或 b  中的元素。也就是说为了保证功能的正确性,编译器不能将a[0]和b[0]装载到寄存器中,相乘它们和将结果存储到c[0]和c[1]中,因为当a[0]与c[0]的存储在同一个地方的时候,结果不同于这个抽象执行模型。所以编译器就没有公共子表达的优势了。同样的,编译器不能进行重排序c[4]的计算结果到c[0]和c[1]计算的接近位置,因为对c[3]的写操作可能会改变c[4]计算的输入。

通过设定a,b,c为受限指针,程序员可以对编译器说指针事实上不需要别名,也就是说通过对c的写操作不会重写a和b中的元素。改变后的函数原型为:

  1. void foo(const float* __restrict__ a,const float* __restrict__ b,float* __restrict__ c);
  1. 注意到所有的指针参数需要被设置成受限的,增加这个关键字,编译器现在可以重排序并且使用公共子表达消除了,同时保证在抽象执行模型的功能统一性:void foo(const float* __restrict__ a,
  2. const float* __restrict__ b,
  3. float* __restrict__ c)
  4. {
  5. float t0 = a[0];
  6. float t1 = b[0];
  7. float t2 = t0 * t2;
  8. float t3 = a[1];
  9. c[0] = t2;
  10. c[1] = t2;
  11. c[4] = t2;
  12. c[2] = t2 * t3;
  13. c[3] = t0 * t3;
  14. c[5] = t1;
  15. ...
  16. }

这里的效果是减少了存储器的访问次数和减少了计算的次数。这里是通过“cached”的装载和公共子表达来增加了寄存器的压力从而平衡得到的。

因为在许多CUDA代码中寄存器压力是一个关键性问题,使用受限指针,减少了占用,所以会在CUDA代码中有着负面性能效果。

notes:在《大规模并行处理器编程实战》第44页说每个块总有有512个线程(不同显卡不同,780ti的是1024);而在《gpu高性能编程cuda实战》中的33页说,启动线程块数组,数组的每一维最大不能超过65535(也是不确定的),而不论是块还是线程数量,都是具有dim3类型的,也就是有x,y,z三个维度。

kernel<<<nblock,nthread>>>(args);这是核函数,告诉运行时创建的函数会有多少个块,每个块有多少个线程。一个核函数就代表一个网格。

上面的意思就是在一个网格中可以有65535×65535×65535个块,每个块最多有8×8×8个线程。(这个说法是错的,在使用matlab的gpuDevice函数返回的结果,其实cuda自己也有查看函数,不过可以从matlab的gpuDevice结果上看,780ti显卡每个块最大线程数1024,但是三个方向上分别最大值不能超过【1024,1024,64】,也就是说我们给定一个块的并发线程总的不能超过1024,意思就是达不到1024×1024×64的结果,而每个网格的最大块数为【2.1475e+09,65535,65535】,所以这些数据不同显卡不同,得及时查证才是)

CUDA1.1-函数类型限定符与变量类型限定符的更多相关文章

  1. C语言函数返回值和变量类型

    前言 最近在刷题,在写矩阵的快速幂的题时,对于返回值是数组的程序,写的十分冗杂.借此机会,重新梳理下C语言中函数的返回值与变量类型的关系. 按照变量的寿命,可以分为三种类型 1.静态变量 寿命从程序开 ...

  2. bash脚本编程之一 变量、变量类型等

    变量的内容 1.变量命名:            1.只能包含字母.数字和下划线,并且不能以数字开头,    2.不应该跟系统中已有的环境变量重名    3.最好能见名知意 2.变量赋值: 设置变量: ...

  3. 如何判断c语言的变量类型

    变量三要素: 一个变量有三个基本的要素,变量的名称,变量的类型,变量的值.所以int a = 10; 变量名为a,变量的存储类型为int型,变量的值为10. 变量还有一些属性如作用范围和存储类型. 变 ...

  4. python应用(5):变量类型与数据结构

    如前所说,写程序如同给算法写壳,而算法就是流程,所以流程是程序的主角(但这个流程不一定要你来设计).在程序中,为了配合流程(算法)的实现,除了顺序.分支与循环语句的使用,还要借助"变量&qu ...

  5. C语言变量 类型判断

    变量三要素: 一个变量有三个基本的要素,变量的名称,变量的类型,变量的值.所以int a = 10; 变量名为a,变量的存储类型为int型,变量的值为10. 变量还有一些属性如作用范围和存储类型. 变 ...

  6. js变量类型及检查

    一.变量的类型 JavaScript 有六种数据类型.主要的类型有 Number.String.object 以及 Boolean 类型,其他两种类型为 null 和 undefined.var ob ...

  7. 010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二——变量类型——即Java中的数据类型

    010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二--变量类型--即Java中的数据类型 Java中变量的三要素 变量名 变 ...

  8. GPU编程自学6 —— 函数与变量类型限定符

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  9. 变量和基本类型——复合类型,const限定符,处理类型

    一.复合类型 复合类型是指基于其他类型定义的类型.C++语言有几种复合类型,包括引用和指针. 1.引用 引用并非对象,它只是为一个已存在的对象所起的另外一个名字. 除了以下2种情况,其他所有引用的类型 ...

随机推荐

  1. java获取当前星期几

    //获取当前星期几Calendar calendar;calendar = Calendar.getInstance();System.out.println(calendar);System.out ...

  2. XML语言基础1

    这学期选修了XML技术这门课,没有发课本,于是参考了W3school教程,整理一下上课的内容. 1.XML简介 XML是一种标记语言,很类似HTML,它不是对HTML的替代,而是对HTML的补充.在大 ...

  3. Bigfish的重新安装

    昨天把ofbiz安装并成功运行起来,今天接着再把bigfish下载下来. 上次安装配置bigfish时,记得是费了一番周折的,最后也不知道怎么瞎折腾,最终居然跑起来了. 今天有意识整理了下关键步骤,记 ...

  4. 使用SQL检测死锁

    第一步:首先创建两个测试表,表goods_sort和goods 表goods_sort:创建并写入测试数据 IF EXISTS(SELECT name FROM sysobjects WHERE na ...

  5. Idea15/16 配置Tomcat

    1:我这里使用的是Eclipse创建的项目,导出后导入到Idea中使用 2:Idea导入Eclipse项目 3:Idea项目配置(默认快捷键CTRL+SHIT+ALT+S) 3.1 Project 项 ...

  6. [转]Zabbix 3.0 安装笔记

    Zabbix 3.0 只支持CentOS 7.0以上版本,所以先在虚拟机中安装好CentOS 7.0 x64,并设置好IP,允许虚拟机联网. 1.安装MySQL 从最新版本的linux系统开始,默认的 ...

  7. ASP.NET Web API 安全筛选器

    原文:https://msdn.microsoft.com/zh-cn/magazine/dn781361.aspx 身份验证和授权是应用程序安全的基础.身份验证通过验证提供的凭据来确定用户身份,而授 ...

  8. [转]Dropdownlist doesn't postback after Page_ClientValidate()

    本文转自:http://stackoverflow.com/questions/2083929/dropdownlist-doesnt-postback-after-page-clientvalida ...

  9. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  10. 淘宝美工一站式:淘宝ps高级美工技巧视频教程,HTML代码学习【教程下载

    视频免费下载地址:http://www.fu83.cn/thread-243-1-1.html