CUDA编程(二) CUDA初始化与核函数
CUDA编程(二)
CUDA初始化与核函数
CUDA初始化
在上一次中已经说过了,CUDA成功安装之后,新建一个project还是十分简单的,直接在新建项目的时候选择NVIDIA CUDA项目就能够了,我们先新建一个MyCudaTest project。删掉自带的演示样例kernel.cu。然后新建项,新建一个CUDA C/C++ File ,我们首先看一下怎样初始化CUDA,因此我命名为InitCuda.cu
首先我们要使用CUDA的RunTime API 所以 我们须要include cuda_runtime.h
#include <stdio.h>
//CUDA RunTime API
#include <cuda_runtime.h>
接下来这个函数会调用 runtime API 中 有关初始化CUDA的内容
//CUDA 初始化
bool InitCUDA()
{
int count;
//取得支持Cuda的装置的数目
cudaGetDeviceCount(&count);
//没有符合的硬件
if (count == 0) {
fprintf(stderr, "There is no device.\n");
return false;
}
int i;
for (i = 0; i < count; i++) {
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if (prop.major >= 1) {
break;
}
}
}
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
cudaSetDevice(i);
return true;
}
这段程序首先会调用cudaGetDeviceCount 函数。获得支持 CUDA 的GPU的数量,假设计算机上没有支持 CUDA 的装置,则会传回 1,而这个1是device 0 ,device0 仅仅是一个仿真装置,可是CUDA的非常多功能都不支持(不支持CUDA1.0以上版本号),因此我们要真正确定系统上是否有支持CUDA的装置,须要对每一个device调用cudaGetDeviceProperties,来获得它们的详细參数,以及所支持的CUDA版本号(prop.major 和 prop.minor 分别代表装置支持的版本号号码,比如 6.5 则 prop.major 为 6 而prop.minor 为 5)
cudaGetDeviceProperties除了能够获得装置支持的 CUDA 版本号之外,还有装置的名称、内存的大小、最大的 thread 数目、运行单元的频率等等。详情可參考NVIDIA 的 CUDA Programming Guide。
在找到支持 CUDA 1.0 以上的装置之后。就能够呼叫 cudaSetDevice 函式,把它设为眼下要使用的显卡。
以下我们在Main函数中调用InitCUDA函数,由于我们使用VS,所以直接ctrl+F5编译运行就能够了。运行时假设系统上有支持 CUDA 的装置。应该会显示 CUDA initialized。
int main()
{
if (!InitCUDA())
{
return 0;
}
printf("CUDA initialized.\n"); return 0;
}
完整程序:
#include <stdio.h>
//CUDA RunTime API
#include <cuda_runtime.h>
//CUDA 初始化
bool InitCUDA()
{
int count;
//取得支持Cuda的装置的数目
cudaGetDeviceCount(&count);
//没有符合的硬件
if (count == 0) {
fprintf(stderr, "There is no device.\n");
return false;
}
int i;
for (i = 0; i < count; i++) {
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if (prop.major >= 1) {
break;
}
}
}
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
cudaSetDevice(i);
return true;
}
int main()
{
if (!InitCUDA())
{
return 0;
}
printf("CUDA initialized.\n"); return 0;
}
CUDA核函数
完毕了CUDA的初始化检查操作,以下我们就能够使用CUDA完毕一些简单计算了。这里我们打算计算一系列数字的立方和。
所以我们先写了一个随机函数:
#define DATA_SIZE 1048576
int data[DATA_SIZE];
//产生大量0-9之间的随机数
void GenerateNumbers(int *number, int size)
{
for (int i = 0; i < size; i++) {
number[i] = rand() % 10;
}
}
//生成随机数(main中调用)
//GenerateNumbers(data, DATA_SIZE);
该函数会产生一大堆 0 ~ 9 之间的随机数,然后我们要对他们进行立方和操作。
那么我们怎样让这个工作在显卡上完毕呢?首先第一件事非常显而易见,这些数字不能放在内存里了,而是要拷贝到GPU的显存上。以下我们就来看一下数据复制的部分。
Host&Device架构:
上一次已经讲过关于CUDA架构的一些基础了。这里再略微复习一下。在 CUDA 的架构下,一个程序分为两个部份:host 端和 device 端。Host 端是指在 CPU 上运行的部份,而 device 端则是在显示芯片上运行的部份。Device 端的程序又称为 “kernel”。通常 host 端程序会将数据准备好后,拷贝到显卡的内存中,再由显示芯片运行 device 端程序。完毕后再由 host 端程序将结果从显卡的内存中取回。
我们须要把产生的数据拷贝到Device端的RAM,才干在显卡上完毕计算。因此我们首先开辟一块合适的显存。然后把随机数从内存复制进去。
//生成随机数
GenerateNumbers(data, DATA_SIZE);
/*把数据拷贝到显卡内存中*/
int* gpudata, *result;
//cudaMalloc 取得一块显卡内存 ( 当中result用来存储计算结果 )
cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
cudaMalloc((void**)&result, sizeof(int));
//cudaMemcpy 将产生的随机数拷贝到显卡内存中
//cudaMemcpyHostToDevice - 从内存拷贝到显卡内存
//cudaMemcpyDeviceToHost - 从显卡内存拷贝到内存
cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE,cudaMemcpyHostToDevice);
凝视已经写得比較明确了。cudaMalloc 和 cudaMemcpy 的使用方法和一般的 malloc 及 memcpy 相似,只是 cudaMemcpy 则多出一个參数,指示复制内存的方向。
在这里由于是从主内存拷贝到显卡内存。所以使用 cudaMemcpyHostToDevice。假设是从显卡内存到主内存,则使用cudaMemcpyDeviceToHost。
完毕了从内存到显存的数据拷贝之后,我们接下来就要在显卡上完毕计算了,怎样让程序跑在显卡上?答案是核函数。
CUDA核函数:
要写在显示芯片上运行的程序。在 CUDA 中,在函数前面加上__global__
表示这个函式是要在显示芯片上运行的,所以我们仅仅要在正常函数之前加上一个__global__
即可了:
// __global__ 函数 (GPU上运行) 计算立方和
__global__ static void sumOfSquares(int *num, int* result)
{
int sum = 0;
int i;
for (i = 0; i < DATA_SIZE; i++) {
sum += num[i] * num[i] * num[i];
}
*result = sum;
}
在显示芯片上运行的程序有一些限制,首先最明显的一个限制——不能有传回值。另一些其它的限制,后面会慢慢提到。
运行核函数:
写好核函数之后须要让CUDA运行这个函数。
在 CUDA 中,要运行一个核函数,使用以下的语法:
函数名称<<<block 数目, thread 数目, shared memory 大小>>>(參数...);
这里我们先不去并行,仅仅是单纯地完毕GPU计算,所以我们让block = 1。thread = 1,share memory = 0
sumOfSquares<<<1, 1, 0>>>(gpudata, result);
计算完了,千万别忘了还要把结果从显示芯片复制回主内存上。然后释放掉内存~
int sum;
//cudaMemcpy 将结果从显存中复制回内存
cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
//Free
cudaFree(gpudata);
cudaFree(result);
最后我们把结果打印出来就大功告成了:
printf("GPUsum: %d \n", sum);
之后我们再用CPU计算一下来验证一下上面的过程是否有错,这一步还是十分必要的:
sum = 0;
for (int i = 0; i < DATA_SIZE; i++) {
sum += data[i] * data[i] * data[i];
}
printf("CPUsum: %d \n", sum);
完整程序:
程序代码:
#include <stdio.h>
#include <stdlib.h>
//CUDA RunTime API
#include <cuda_runtime.h>
#define DATA_SIZE 1048576
int data[DATA_SIZE];
//产生大量0-9之间的随机数
void GenerateNumbers(int *number, int size)
{
for (int i = 0; i < size; i++) {
number[i] = rand() % 10;
}
}
//CUDA 初始化
bool InitCUDA()
{
int count;
//取得支持Cuda的装置的数目
cudaGetDeviceCount(&count);
if (count == 0) {
fprintf(stderr, "There is no device.\n");
return false;
}
int i;
for (i = 0; i < count; i++) {
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if (prop.major >= 1) {
break;
}
}
}
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
cudaSetDevice(i);
return true;
}
// __global__ 函数 (GPU上运行) 计算立方和
__global__ static void sumOfSquares(int *num, int* result)
{
int sum = 0;
int i;
for (i = 0; i < DATA_SIZE; i++) {
sum += num[i] * num[i] * num[i];
}
*result = sum;
}
int main()
{
//CUDA 初始化
if (!InitCUDA()) {
return 0;
}
//生成随机数
GenerateNumbers(data, DATA_SIZE);
/*把数据拷贝到显卡内存中*/
int* gpudata, *result;
//cudaMalloc 取得一块显卡内存 ( 当中result用来存储计算结果 )
cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
cudaMalloc((void**)&result, sizeof(int));
//cudaMemcpy 将产生的随机数拷贝到显卡内存中
//cudaMemcpyHostToDevice - 从内存拷贝到显卡内存
//cudaMemcpyDeviceToHost - 从显卡内存拷贝到内存
cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
// 在CUDA 中运行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(參数...);
sumOfSquares << <1, 1, 0 >> >(gpudata, result);
/*把结果从显示芯片复制回主内存*/
int sum;
//cudaMemcpy 将结果从显存中复制回内存
cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
//Free
cudaFree(gpudata);
cudaFree(result);
printf("GPUsum: %d \n", sum);
sum = 0;
for (int i = 0; i < DATA_SIZE; i++) {
sum += data[i] * data[i] * data[i];
}
printf("CPUsum: %d \n", sum);
return 0;
}
运行结果:
总结:
这次给大家介绍了CUDA的初始化和怎样在显卡上运行程序。即先将数据从内存拷贝到显存。再写好运算的核函数,之后用CUDA调用核函数,完毕GPU上的计算。之后当然不要忘记将结果复制回内存,释放掉显存。
总的来说一个CUDA程序的骨架已经搭建起来了,而GPU计算的重中之重即并行加速还没有进行介绍,只是在加速之前我们另一件非常重要的事情须要考虑,那就是我们的程序究竟有没有加速,也就是我们要输出程序的运行时间,这个时间我们须要使用CUDA提供的一个Clock函数,能够取得GPU运行单元的频率,所以下一篇博客我将主要解说这个函数~希望能给大家的学习带来帮助~
參考资料:《深入浅出谈CUDA》
CUDA编程(二) CUDA初始化与核函数的更多相关文章
- CUDA编程->CUDA入门了解(一)
安装好CUDA6.5+VS2012,操作系统为Win8.1版本号,首先下个GPU-Z检測了一下: 看出本显卡属于中低端配置.关键看两个: Shaders=384.也称作SM.或者说core/流处理器数 ...
- CUDA编程之快速入门
CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架构.做图像视觉领域的同学多多少少都会接触到CUDA,毕竟要做性能速度优化,CUDA是个很重要 ...
- CUDA编程之快速入门【转】
https://www.cnblogs.com/skyfsm/p/9673960.html CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架 ...
- cuda编程基础
转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...
- CUDA刷新器:CUDA编程模型
CUDA刷新器:CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA,CUDA刷新器,并行编程 这是CUDA更新系列的第四篇文章,它的目标是 ...
- CUDA编程模型
1. 典型的CUDA编程包括五个步骤: 分配GPU内存 从CPU内存中拷贝数据到GPU内存中 调用CUDA内核函数来完成指定的任务 将数据从GPU内存中拷贝回CPU内存中 释放GPU内存 *2. 数据 ...
- CUDA编程学习笔记1
CUDA编程模型是一个异构模型,需要CPU和GPU协同工作. host和device host和device是两个重要的概念 host指代CPU及其内存 device指代GPU及其内存 __globa ...
- 不同版本CUDA编程的问题
1 无法装上CUDA的toolkit 卸载所有的NVIDIA相关的app,包括NVIDIA的显卡驱动,然后重装. 2之前的文件打不开,one or more projects in the solut ...
- CUDA编程
目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. ...
随机推荐
- netty 引用计数器 ,垃圾回收
netty 引用计数器 ,垃圾回收 https://blog.csdn.net/u013851082/article/details/72170065 Netty之有效规避内存泄漏 https://w ...
- 1807. [NOIP2014]寻找道路P2296 寻找道路
题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条 ...
- P1400 塔
题目描述 有N(2<=N<=600000)块砖,要搭一个N层的塔,要求:如果砖A在砖B上面,那么A不能比B的长度+D要长.问有几种方法,输出 答案 mod 1000000009的值. 输入 ...
- ES6特性之模块【Modules】
ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步加载 ...
- FTP初始化文件.netrc使用技巧[转发]
FTP初始化文件.netrc使用技巧 FTP(文件传输)和E-mail(电子邮件).Telnet(远程登录)一样,是 Internet的三大主要功能之一.因为使用频繁,用户往往会遇到各种 各样的问题, ...
- iOS中ARC和非ARC混用
如果在使用第三方类库的时候,我们可能会遇到一些内存管理的问题 那么如何在一个工程中实现ARC和非ARC混用呢,例如你创建一个ARC的工程,但是你引用的第三方类库是非ARC管理内存的 首先点击工 ...
- 音视频】5.ffmpeg命令分类与使用
GT其实平时也有一些处理音视频的个人或者亲人需求,熟练使用ffmpeg之后也不要借助图示化软件,一个命令基本可以搞定 G: 熟练使用ffmpeg命令!T :不要死记硬背,看一遍,自己找下规律,敲一遍, ...
- CAD梦想看图6.0安卓版详情介绍
下载安装 MxCAD6.0(看图版).2018.10.22更新,扫描下面二维码,安装CAD梦想看图: 下载地址: http://www.mxdraw.com/help_8_20097.html 软 ...
- 06Oracle Database 数据类型
Oracle Database 数据类型 字符型 char(n)最大2000个字节 定长 nchar(n)最大2000个字节 变长 varchar2(n) 最大4000个字节 变长 nvarchar2 ...
- HEVC-HM16.9源码学习(1)TEncCu::xCompressCU
函数入口:Void TEncSlice::compressSlice的m_pcCuEncoder->compressCtu( pCtu );调用xCompressCU( m_ppcBestCU[ ...