http://blog.csdn.net/yutianzuijin/article/details/8147912

分类: 编程语言2012-11-05 10:55 2521人阅读 评论(0) 收藏 举报

最近初试cuda编程,作为一个新手,遇到了各种各样的问题,然后花费了大量时间解决这些匪夷所思的问题。为了避免后来人重蹈覆辙,现把自己遇到的问题总结如下。

(一)、cudaMalloc

初次使用该函数,感觉没有什么困难,和c语言的malloc类似。但是在具体应用中却出了一个很难找的错误,花费了很多时间。该函数使用是需要注意的就是,它分配的内存空间单位是字节,所以需要我们在使用时用sizeof指定具体分配的变量类型,这样才能正确分配空间。例:

cudaMalloc((void**)&gpu_data,sizeof(float)*1024);

(二)、函数的执行位置

cuda程序的一大特色是程序的核心部分在GPU上执行,所以cuda函数就分为不同的类别:host、global、device三类。所以我们在编写函数时一定要分清楚当前正在编写的是哪类函数,可以调用什么库函数。

  • host函数:在CPU上调用,在CPU上执行,可以调用global函数,不能调用device函数;
  • global函数:只能在host函数中调用,但是执行是在GPU上执行,例如cudaMalloc之类的内存操作库函数,可以调用device函数;
  • device函数:只能在GPU上调用和执行,只能被global函数引用。
    关于函数类别容易出现的错误就是内存分配时CPU和GPU的混淆。我们只需要记住,在host函数中可以直接使用的内存都是CPU上的内存,GPU上的内存需要通过cudaMemcpy函数调用拷贝到CPU内存空间;在global和device函数中使用的内存都是在GPU内存空间,使用之前需要分配。
 
(三)、共享内存
     共享内存是提升程序性能很重要的一部分,能不能用好共享内存是是否掌握cuda编程的一个重要依据。在此只想强调一点:共享内存没有初始化!下面是自己写的一个数组求和程序,用到了共享内存:
  1. __device__ int count=0;
  2. __global__ static void sum(int* data_gpu,int* block_gpu,int *sum_gpu,int length)
  3. {
  4. extern __shared__ int blocksum[];
  5. __shared__ int islast;
  6. int offset;
  7. const int tid=threadIdx.x;
  8. const int bid=blockIdx.x;
  9. blocksum[tid]=0;
  10. for(int i=bid*THREAD_NUM+tid;i<length;i+=BLOCK_NUM*THREAD_NUM)
  11. {
  12. blocksum[tid]+=data_gpu[i];
  13. }
  14. __syncthreads();
  15. offset=THREAD_NUM/2;
  16. while(offset>0)
  17. {
  18. if(tid<offset)
  19. {
  20. blocksum[tid]+=blocksum[tid+offset];
  21. }
  22. offset>>=1;
  23. __syncthreads();
  24. }
  25. if(tid==0)
  26. {
  27. block_gpu[bid]=blocksum[0];
  28. __threadfence();
  29. int value=atomicAdd(&count,1);
  30. islast=(value==gridDim.x-1);
  31. }
  32. __syncthreads();
  33. if(islast)
  34. {
  35. if(tid==0)
  36. {
  37. int s=0;
  38. for(int i=0;i<BLOCK_NUM;i++)
  39. {
  40. s+=block_gpu[i];
  41. }
  42. *sum_gpu=s;
  43. }
  44. }
  45. }
   特别注意第11八行代码,不对要访问的共享内存进行初始化将得不到正确的结果。
 
(四)、原子函数调用
   在调用原子函数时,需要指定当前显卡的计算能力,否则会报错“atomic*** is undefined.”。 linux下解决方案是在编译源代码时为nvcc编译器指定一个计算能力的选项。例如计算能力时1.3,则可以添加参数:-arch sm_13,这样就可以顺利编译。
 
(五)、CUDA语法
   很多参考书都介绍说CUDA采用的是C扩展语法,所以一开始我们很容易认为采用C语法就够了。但是这样也容易让我们陷入一个误区:只能是C语法,而不能是其他。其实CUDA是C和C++的混合体,有时候采用C++的语法会更便利:
  • for循环内可以定义变量,标准C语言不支持,所以我们可以直接用(for int i=0;i<length;i++),这样的好处是可以节省一个寄存器;
  • 变量定义位置无限制,可以在任意位置定义变量;
  • CUDA支持多态,所以我们可以定义多个名称相同,参数不同的函数,这个没有问题;
  • 有时多态可以用模版(template)来合并代码,达到简化编程的目的;
(六)、block和thread号的正确使用
    为了调度不同的线程,我们通常需要利用内置变量threadIdx和blockIdx作为循环中的增量。但是切记在循环内部要正确使用内置变量,两天debug的教训!下面是一个示例代码:
  1. __global__ static void saliencefunc(float *peaks_gpu,int *index_gpu,float *saliencebins_gpu,int framenumber)
  2. {
  3. __shared__ float peaks[HALF_PEAK_NUM];
  4. __shared__ int index[HALF_PEAK_NUM];
  5. int tid=threadIdx.x;
  6. int bid=blockIdx.x;
  7. for(int i=bid;i<framenumber;i+=BLOCK_NUM)
  8. {
  9. if(tid<HALF_PEAK_NUM)
  10. {
  11. peaks[tid]=peaks_gpu[HALF_PEAK_NUM*i+tid];
  12. index[tid]=index_gpu[HALF_PEAK_NUM*i+tid];
  13. }
  14. __syncthreads();
  15. }
  16. }

注意代码第十三和十四行的赋值操作HALF_PEAK_NUM*i+tid,笔者之前的写法是HALF_PEAK_NUM*bid+tid,结果花了两天的时间找问题,所以要正确使用,在可以替换的情况下就用i或者j这样的变量,尽量少用内置变量。

(七)、空间释放
    在GPU上分配的空间,在使用完成之后要及时释放。对于运行一次的程序,不释放空间没有什么大碍,毕竟程序结束空间自动会被释放掉。但是当程序不间断运行多次的时候,不释放空间会导致非常严重的GPU内存泄露。第一个问题是随着程序的运行,GPU内存耗尽,导致后续内存分配失败;第二个问题是,程序运行会越来越慢。所以我们一定要养成用完及时释放空间的习惯。

CUDA编程常见问题 转的更多相关文章

  1. 不同版本CUDA编程的问题

    1 无法装上CUDA的toolkit 卸载所有的NVIDIA相关的app,包括NVIDIA的显卡驱动,然后重装. 2之前的文件打不开,one or more projects in the solut ...

  2. cuda编程基础

    转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...

  3. CUDA学习笔记(一)——CUDA编程模型

    转自:http://blog.sina.com.cn/s/blog_48b9e1f90100fm56.html CUDA的代码分成两部分,一部分在host(CPU)上运行,是普通的C代码:另一部分在d ...

  4. CUDA编程

    目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. ...

  5. android编程常见问题-程序真机中不显示

    新手编程常见问题: 问题表现:连接上手机后,程序不显示 解决版本:检查AndroidManifest.xml 文件中SDK版本的设置(要求要兼容当前手机版本系统),如下:

  6. android编程常见问题-程序在模拟器中不显示

    新手编程常见问题: 问题表现:程序运行成功,但是在模拟器中不显示 解决办法:检查项目版本和模拟器版本是否匹配或兼容,如果不匹配,选择和模拟器版本一致 项目版本:右键-Properties-androi ...

  7. CUDA编程-(1)Tesla服务器Kepler架构和万年的HelloWorld

    结合CUDA范例精解以及CUDA并行编程.由于正在学习CUDA,CUDA用的比较多,因此翻译一些个人认为重点的章节和句子,作为学习,程序将通过NVIDIA K40服务器得出结果.如果想通过本书进行CU ...

  8. cuda编程(一)

    环境安装和例程运行 显卡主要有两家,ATI.NVIDIA,简称A卡和N卡.随着GPU计算能力的上升,采用GPU并行计算来加速的应用越来越多. Nvidia创立人之一,黄仁勋(Jen-Hsun Huan ...

  9. CUDA编程入门,Dim3变量

    dim3是NVIDIA的CUDA编程中一种自定义的整型向量类型,基于用于指定维度的uint3. 例如:dim3 grid(num1,num2,num3): dim3类型最终设置的是一个三维向量,三维参 ...

随机推荐

  1. pat 1051Pop Sequence

    模拟栈的过程,一开始我是用的cin来判断每行的每一个数字,然后判断回车结束.有一个点会超时,答案用数组先记录序列的方法非常好.有一个注意点就是访问s.top()的时候先要保证s.size()>0 ...

  2. gitlab重置root的密码

    环境:gitlab 忘记了root密码,无法登陆gitlab 解决: gitlab-ctl start 保证gitlab处于启动状态,&保证redis处于启动状态 gitlab-rails c ...

  3. poj1733(并查集+离散化)

    题目大意:有一个长度为n的0,1字符串, 给m条信息,每条信息表示第x到第y个字符中间1的个数为偶数个或奇数个, 若这些信息中第k+1是第一次与前面的话矛盾, 输出k; 思路:x, y之间1的个数为偶 ...

  4. python 构造一个可以返回多个值的函数

    为了能返回多个值,函数直接return 一个元组就行了 看上去返回了多个值,实际上是先创建了一个元组然后返回的.这个语法看上去比较奇怪,实际上我们使用的是逗号来生成一个元组,而不是用括号 >&g ...

  5. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  6. C++ STL 顺序容器--list + 关联容器

    list 双向链表,可以双向遍历,既指向前驱节点,又指向后继但不能随机访问任意元素,可动态增加或者减少元素,内存管理自动完成,增加任何元素都不会使迭代器失效, 删除元素时,除了指向当前被删元素的迭代器 ...

  7. python爬虫——与不断变化的页面死磕和更新换代(3)

    经过上一次的实战,手感有了,普罗西(雾)池也有了,再战taobao/tmall 试着使用phantomJS爬手机端,结果发现爬来的tmall页面全是乱码,taobao页面xpath识别错误.一顿分析了 ...

  8. CSS Class 选择器

    CSS Class 选择器 再<stype>标签内,通过class属性来对标签进行css样式配置 <html> <head> <!-- style 设置头部标 ...

  9. Linux LVM卷组管理

    Linux LVM卷组管理 由于传统的磁盘管理不能对磁盘进行磁盘管理,因此诞生了LVM技术,LVM技术最大的特点就是对磁盘进行动态管理. 由于LVM的逻辑卷的大小更改可以进行动态调整,且不会出现丢失数 ...

  10. 2的n次方用c语言怎么表示

    C语言有函数,需要头文件#include <math.h>用pow(2,n)就可以了! double result = pow(2,n);