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. ...
随机推荐
- Vue组件之间通信的三种方式
最近在看梁颠编著的<Vue.js实战>一书,感觉颇有收获,特此记录一些比价实用的技巧. 组件是MVVM框架的核心设计思想,将各功能点组件化更利于我们在项目中复用,这类似于我们服务端面向对象 ...
- 专题九:实现类似QQ的即时通信程序
引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net类库去实现它们的.为了让大家更好的理解我们平常中常见的软件QQ的工作原理,所以在 ...
- 60查找nanopim1plus的HDMI为720p输出的问题
60查找nanopim1plus的HDMI为720p输出的问题 大文实验室/大文哥 壹捌陆捌零陆捌捌陆捌贰 21504965 AT qq.com 完成时间:2017/12/5 17:51 版本:V1. ...
- Farseer.net轻量级开源框架 中级篇:执行SQL语句
导航 目 录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 中级篇: 事务的使用 下一篇:Farseer.net轻量级开源框架 中级篇: DbFacto ...
- jQuery 遍历 - children() 方法
jQuery 遍历参考手册 实例 找到类名为 "selected" 的所有 div 的子元素,并将其设置为蓝色: $("div").children(" ...
- js获取图片信息(一)-----获取图片的原始尺寸
如何获取图片的原始尺寸大小? 如下,当给 img 设置一个固定的大小时,要怎样获取图片的原始尺寸呢? #oImg{ width: 100px; height: 100px; } <img src ...
- Java8(一)--lambda表达式
相信作为一个Java程序员都会或多或少的了解过Java8中的lambda表达式.函数式编程等,本人也是用过lambda表达式,使用的都是比较简单 的实现 通过一个例子去都感受lambda: Compa ...
- centos7安装个人博客wordpress
第一步: 安装apache web 第二步: 安装MariaDB数据库 第三步: 安装PHP 第四步: 安装phpMyAdmin 第五部: 创建数据库: 第六部: 下载wordpress 第七部:复制 ...
- Install Zabbix with Docker
1. mysql -uroot -p -h10.10.0.242 zabbix<schema.sqlEnter password: * ERROR 1709 (HY000) at line 86 ...
- NAT、NAPT(PAT)的基本概念和工作原理及区别
转自:http://blog.sina.com.cn/s/blog_5d302bd20100gprv.html 近年来,随着 Internet 的迅猛发展,连入 Internet 的主机数量成倍增长. ...