CUDA 编程的基本模式
reproduced from: http://www.cnblogs.com/muchen/p/6306747.html
前言
本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的。
模式描述
1. 定义需要在 device 端执行的核函数。( 函数声明前加 _golbal_ 关键字 )
2. 在显存中为待运算的数据以及需要存放结果的变量开辟显存空间。( cudaMalloc 函数实现 )
3. 将待运算的数据传输进显存。( cudaMemcpy,cublasSetVector 等函数实现 )
4. 调用 device 端函数,同时要将需要为 device 端函数创建的块数线程数等参数传递进 <<<>>>。( 注: <<<>>>下方编译器可能显示语法错误,不用管 )
5. 从显存中获取结果变量。( cudaMemcpy,cublasGetVector 等函数实现 )
6. 释放申请的显存空间。( cudaFree 实现 )
PS:每个 device 端函数在被调用时都能获取到调用它的具体块号,线程号,从而实现并行( 获取方法请参考下面的编程规范说明以及代码示例 )。
编程规范说明
在 CUDA 标准编程模式中,增加了一些编程规范,在这里简要说明:
函数声明关键字:
1. __device__
表明此函数只能在 GPU 中被调用,在 GPU 中执行。这类函数只能被 __global__ 类型函数或 __device__ 类型函数调用。
2. __global__
表明此函数在 CPU 上调用,在 GPU 中执行。这也是以后会常提到的 "内核函数",有时为了便于理解也称 "device" 端函数。
3. __host__
表明此函数在 CPU 上调用和执行,这也是默认情况。
内核函数配置运算符 <<<>>> - 这个运算符在调用内核函数的时候使用,一般情况下传递进三个参数:
1. 块数
2. 线程数
3. 共享内存大小 (此参数默认为0 )
内核函数中的几个系统变量 - 这几个变量可以在内核函数中使用,从而控制块与线程的工作:
1. gridDim:块数
2. blockDim:块中线程数
3. blockIdx:块编号 (0 - gridDim-1)
4. threadIdx:线程编号 (0 - blockDim-1)
知道这些已经足够编写 CUDA 程序了,更多的编程说明将在以后的文章中介绍。
代码示例
该程序采用 CUDA 并行化思想来对数组进行求和:
// 相关 CUDA 库
#include "cuda_runtime.h"
#include "cuda.h"
#include "device_launch_parameters.h" #include <iostream>
#include <cstdlib> using namespace std; const int N = ; // 块数
const int BLOCK_data = ;
// 各块中的线程数
const int THREAD_data = ; // CUDA初始化函数
bool InitCUDA()
{
int deviceCount; // 获取显示设备数
cudaGetDeviceCount (&deviceCount); if (deviceCount == )
{
cout << "找不到设备" << endl;
return EXIT_FAILURE;
} int i;
for (i=; i<deviceCount; i++)
{
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop,i)==cudaSuccess) // 获取设备属性
{
if (prop.major>=) //cuda计算能力
{
break;
}
}
} if (i==deviceCount)
{
cout << "找不到支持 CUDA 计算的设备" << endl;
return EXIT_FAILURE;
} cudaSetDevice(i); // 选定使用的显示设备 return EXIT_SUCCESS;
} // 此函数在主机端调用,设备端执行。
__global__
static void Sum (int *data,int *result)
{
// 取得线程号
const int tid = threadIdx.x;
// 获得块号
const int bid = blockIdx.x; int sum = ; // 有点像网格计算的思路
for (int i=bid*THREAD_data+tid; i<N; i+=BLOCK_data*THREAD_data)
{
sum += data[i];
} // result 数组存放各个线程的计算结果
result[bid*THREAD_data+tid] = sum;
} int main ()
{
// 初始化 CUDA 编译环境
if (InitCUDA()) {
return EXIT_FAILURE;
}
cout << "成功建立 CUDA 计算环境" << endl << endl; // 建立,初始化,打印测试数组
int *data = new int [N];
cout << "测试矩阵: " << endl;
for (int i=; i<N; i++)
{
data[i] = rand()%;
cout << data[i] << " ";
if ((i+)% == ) cout << endl;
}
cout << endl; int *gpudata, *result; // 在显存中为计算对象开辟空间
cudaMalloc ((void**)&gpudata, sizeof(int)*N);
// 在显存中为结果对象开辟空间
cudaMalloc ((void**)&result, sizeof(int)*BLOCK_data*THREAD_data); // 将数组数据传输进显存
cudaMemcpy (gpudata, data, sizeof(int)*N, cudaMemcpyHostToDevice);
// 调用 kernel 函数 - 此函数可以根据显存地址以及自身的块号,线程号处理数据。
Sum<<<BLOCK_data,THREAD_data,>>> (gpudata,result); // 在内存中为计算对象开辟空间
int *sumArray = new int[THREAD_data*BLOCK_data];
// 从显存获取处理的结果
cudaMemcpy (sumArray, result, sizeof(int)*THREAD_data*BLOCK_data, cudaMemcpyDeviceToHost); // 释放显存
cudaFree (gpudata);
cudaFree (result); // 计算 GPU 每个线程计算出来和的总和
int final_sum=;
for (int i=; i<THREAD_data*BLOCK_data; i++)
{
final_sum += sumArray[i];
} cout << "GPU 求和结果为: " << final_sum << endl; // 使用 CPU 对矩阵进行求和并将结果对照
final_sum = ;
for (int i=; i<N; i++)
{
final_sum += data[i];
}
cout << "CPU 求和结果为: " << final_sum << endl; getchar(); return ;
}
小结
1. 掌握本节知识的关键除了要掌握各个API,还要深刻理解内核函数中的块及线程变量的控制,或者说施展 :)
2. 一定要明确传递进 API 的是参数本身,还是参数的地址,这很关键。
CUDA 编程的基本模式的更多相关文章
- 不同版本CUDA编程的问题
1 无法装上CUDA的toolkit 卸载所有的NVIDIA相关的app,包括NVIDIA的显卡驱动,然后重装. 2之前的文件打不开,one or more projects in the solut ...
- cuda编程基础
转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...
- CUDA学习笔记(一)——CUDA编程模型
转自:http://blog.sina.com.cn/s/blog_48b9e1f90100fm56.html CUDA的代码分成两部分,一部分在host(CPU)上运行,是普通的C代码:另一部分在d ...
- CUDA编程
目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. ...
- CUDA编程-(1)Tesla服务器Kepler架构和万年的HelloWorld
结合CUDA范例精解以及CUDA并行编程.由于正在学习CUDA,CUDA用的比较多,因此翻译一些个人认为重点的章节和句子,作为学习,程序将通过NVIDIA K40服务器得出结果.如果想通过本书进行CU ...
- cuda编程(一)
环境安装和例程运行 显卡主要有两家,ATI.NVIDIA,简称A卡和N卡.随着GPU计算能力的上升,采用GPU并行计算来加速的应用越来越多. Nvidia创立人之一,黄仁勋(Jen-Hsun Huan ...
- CUDA编程入门,Dim3变量
dim3是NVIDIA的CUDA编程中一种自定义的整型向量类型,基于用于指定维度的uint3. 例如:dim3 grid(num1,num2,num3): dim3类型最终设置的是一个三维向量,三维参 ...
- CUDA编程(六)进一步并行
CUDA编程(六) 进一步并行 在之前我们使用Thread完毕了简单的并行加速,尽管我们的程序运行速度有了50甚至上百倍的提升,可是依据内存带宽来评估的话我们的程序还远远不够.在上一篇博客中给大家介绍 ...
- CUDA编程模型之内存管理
CUDA编程模型假设系统是由一个主机和一个设备组成的,而且各自拥有独立的内存. 主机:CPU及其内存(主机内存),主机内存中的变量名以h_为前缀,主机代码按照ANSI C标准进行编写 设备:GPU及其 ...
随机推荐
- pm2操作总结
PM2是一个node.js的进程管理器,(并且呢在应用程序的生产运行时自带负载均衡的这种操作,很厉害): --> pm2主要解决的问题是kill node进程时无法正常停止的问题. 主要特征: ...
- Android dialog使用
翻译自:开发->API 指南->User Interface & Navigation->Dialogs 注意: dialog是一个基类,但是我们应该尽可能避免直接使用dia ...
- python的type class
在python中,用户定义的class是一个PyTypeObject ( XXX_Type)对象. #PyType_Type是一切类的基类,这是一个全局数据PyTypeObject PyType_Ty ...
- LinkedHashMap结构get和put源码流程简析及LRU应用
原理这篇讲得比较透彻Java集合之LinkedHashMap. 本文属于源码阅读笔记,因put,get调用逻辑及链表维护逻辑复杂(至少网上其它文章的逻辑描述及配图,我都没看明白LinkedHashMa ...
- Spring Cloud (6)A config 服务端配置 与github通信
1. 在git上创建一个库,并复制库地址 2.用git clone项目到本地,并创建application.yml application.yml 内容 --下一步 3.将application.ym ...
- crontab 安装与配置
安装方法: yum -y install vixie-cron yum -y install crontabs 说明:vixie-cron 软件包是 cron 的主程序:crontabs 软件包是用来 ...
- centos7 自动定时备份mysql数据库
shell脚本:mysqlbak.sh #!/bin/bashPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbinexpo ...
- Android虚拟机与Java虚拟机 两种虚拟机的比较
在Android的体系框架中有一部分叫做Android Runtime,即Android运行时环境,这个环境包括了两个部分,一个是Android的核心类库,还有一个就是Dalvik虚拟机了. Andr ...
- jqGrid基本使用
jqGrid: 参照网址:官网地址http://www.trirand.com/ http://blog.mn886.net/jqGrid/(快速获取demo) http://www.guriddo. ...
- APP-2.1-Hbuilder与夜神 & HbuilderX与夜神模拟器连接
经上一步完成Hbuilder.HbuilderX与夜神模拟器的安装,本次介绍下两者之间的连接设置. 1.三者的安装路径 Hbuilder:E:\SAP UI5\HBuilder HbuilderX:D ...