深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题。这里主要记录自己的GPU自学历程。

目录

六、 函数与变量类型限定符

在之前的小节中,我们已经遇到了 __global____shared__这两种类型限定符。 前者属于函数类型限定符,后者则属于变量类型限定符。 接下来,我们来来了解一下这两类限定符。

6.1 函数类型限定符

函数类型限定符用来标识函数运行在主机还是设备上,函数由主机还是设备调用。

__global__

  • __global__修饰的函数为 核函数
  • 运行在设备上;
  • 可以由主机调用;
  • 可以由计算能力大于3.2的设备调用;
  • 必须有void返回类型;
  • 调用时必须制定运行参数(<<< >>>)
  • 该函数的调用时异步的,即可以不必等候该函数全部完成,便可以在CPU上继续工作;

__device__

  • 运行在设备上;
  • 只能由设备调用;
  • 编译器会内联所有认为合适的__device__修饰的函数;

__host__

  • 运行在主机上;
  • 只能由主机调用;
  • 效果等同于函数不加任何限定符;
  • 不能与__global__共同使用, 但可以和__device__联合使用;

__noinline__

  • 声明不允许内联

__forceinline__

  • 强制编译器内联该函数

6.2 变量类型限定符

变量类型限定符用来标识变量在设备上的内存位置。

__device__ (单独使用时)

  • 位于 global memory space
  • 生命周期为整个应用期间(即与application同生死)
  • 可以被grid内的所有threads读取
  • 可以在主机中由以下函数读取
    • cudaGetSymbolAddress()
    • cudaGetSymbolSize()
    • cudaMemcpyToSymbol()
    • cudaMemcpyFromSymbol()

__constant__

  • 可以和 __device__ 联合使用
  • 位于 constant memory space
  • 生命周期为整个应用期间
  • 可以被grid内的所有threads读取
  • 可以在主机中由以下函数读取
    • cudaGetSymbolAddress()
    • cudaGetSymbolSize()
    • cudaMemcpyToSymbol()
    • cudaMemcpyFromSymbol()

__shared__

  • 可以和 __device__ 联合使用
  • 位于一个Block的shared memory space
  • 生命周期为整个Block
  • 只能被同一block内的threads读写

__managed__

  • 可以和 __device__ 联合使用
  • 可以被主机和设备引用,主机或者设备函数可以获取其地址或者读写其值
  • 生命周期为整个应用期间

__restrict__

该关键字用来对指针进行限制性说明,目的是为了减少指针别名带来的问题。

C99标准中引入了restricted指针,用以缓解C语言中指针二义性的问题。缓解指针二义性问题可用于编译器的代码优化。下面是一个指针二义性的例子:

void foo(const float* a,
         const float* b,
         float* c)
{
    c[0] = a[0] * b[0];
    c[1] = a[0] * b[0];
    c[2] = a[0] * b[0] * a[1];
    c[3] = a[0] * a[1];
    c[4] = a[0] * b[0];
    c[5] = b[0];
    ...
}

在C语言中,指针a, b, 和c可能有二义性(别名),因而对数组c的写入可能会更改数组a和b的元素的值。这就意味着,为了保证程序的正确性,编译器不能把a[0]和b[0]装载入寄存器,对它们做乘法,然后把结果写入c[0]和c[1],这是因为有这种可能a[0]和c[0]是同一个地址。故而编译器无法对相同的表达式进行优化。

通过把a, b, c声明为restricted指针,程序员可以断言这些指针实际上没有二义性(这里,所有的指针参数都要被设为restrict

void foo(const float*  __restrict__a,
         const float*  __restrict__ b,
               float* __restrict__ c)

在增加了restrict关键字以后,编译器可以根据需要对代码进行优化:

void foo(const float* __restrict__ a,
         const float* __restrict__ b,
         float* __restrict__ c)
{
    float t0 = a[0];
    float t1 = b[0];
    float t2 = t0 * t2;
    float t3 = a[1];
    c[0] = t2;
    c[1] = t2;
    c[4] = t2;
    c[2] = t2 * t3;
    c[3] = t0 * t3;
    c[5] = t1;
    ...
}

这样便可以减少访存次数和计算量,而代价是增加寄存器的使用量。考虑到额外的寄存器使用可能会降低occupancy,因此这种优化也可能会带来负面效果。

参考资料

最新文章

  1. win10删除导航栏文档等图标,去除快捷方式
  2. jquery plugins
  3. HDU1003 Max Sum(求最大字段和)
  4. Converting Storyboard from iPhone to iPad
  5. easyui combobox 模糊检索数据并填充
  6. WPF学习笔记4&mdash;&mdash;Layout之2
  7. Foundation: NSNotificationCenter
  8. sae的kvdb使用注意
  9. 跟踪MYSQL 的查询优化过程方法
  10. Activity生命周期方法的调用顺序project与測试日志
  11. nginx做下载限速
  12. 文章3说话 微信商城云server创建后台
  13. Java获取IP
  14. 判断二叉树是否二叉排序树(BST)
  15. SpringCloud的Hystrix(二) 某消费者应用(如:ui、网关)访问的多个微服务的断路监控
  16. Java多线程_复习(更新中!!)
  17. centos设置路由route
  18. Linux 文件删除原理_009
  19. C#自定义无边框MessageBox窗体
  20. 制作tomcat重启.bat文件

热门文章

  1. mysql字符串分割操作
  2. MR案例:外连接代码实现
  3. [翻译]如何在HTML5中有效使用ARIA
  4. Linux系统CentOS使用yum方式安装指定版本的PHP 添加yum源 从PHP5.3升级到5.4/5.5/5.6
  5. Tornado异步(2)
  6. 2017 ACM/ICPC Asia Regional Qingdao Online - 1008 Chinese Zodiac
  7. from: can&#39;t read /var/mail/xxx 解决方法
  8. .Net Core 修改默认的启动端口
  9. 明确出需求 然后开会评审 要什么接口 接口参数、返回json内容、格式 协定好 在做
  10. Mysql存储过程、索引