CUDA编程模型
1. 典型的CUDA编程包括五个步骤:
分配GPU内存
从CPU内存中拷贝数据到GPU内存中
调用CUDA内核函数来完成指定的任务
将数据从GPU内存中拷贝回CPU内存中
释放GPU内存
*2.
数据局部性:(是指数据重用,以降低对于内存访问的延迟)
时间局部性:指在较短的时间内实现对数据或资源的重用
空间局部性:指在相对较接近的存储空间内数据元素的重用
CPU中通过缓存来增强时间局部性和空间局部性的优化
(不是很懂)3.CUDA中有内存层次和线程层次的概念
内存层次结构
线程层次结构
CUDA编程模型中存在共享内存,通过为主内存节省带宽来大幅度提高运行速度。
4.在CUDA编程中,你只需要编写单个线程需要执行的操作命令,通过调用核函数,GPU通过处理这样的核函数,来启动成千上万的线程来运行这个操作。
5.CUDA开发环境
NVIDIA Nsight 集成开发环境
CUDA-GDB命令行调试器
用于性能分析的可视化和命令行分析器
CUDA-MEMCHECK内存分析器
GPU设备管理工具
6.CUDA编程模型利用GPU架构的计算能力提供了以下几个特有功能:
一种通过层次结构在GPU中组织线程的方法
一种通过层次结构在GPU中访问内存的方法
7.在使用CUDA编程模型的时候,需要注意到
主机:CPU及其内存
设备:GPU及其内存(GPU显存)
8.CUDA6.0开始,NVIDIA推出统一寻址方式,可以通过指针访问主机内存和设备内存
9.内核是CUDA编程模型的重要组成部分,其代码在GPU设备上运行。
10.多数情况下,主机可以独立地对设备进行操作,内核一旦启动,管理权立刻返回给主机,一个典型的CUDA程序包括由并行代码互补的串行代码。
11.CUDA编程模型假设系统是由一个主机和一个设备组成的,而且各自拥有独立的内存,核函数是在设备上运行的,CUDA运行时负责分配和释放设备上的内存,并且在主机内存和设备内存之间进行传输。
12.用于分配设备内存的CUDA函数是
cudaError_t cudaMalloc(void ** devPtr,size_t size);
当分配好内存后,该函数会返回devPtr指针用来指向分配的内存,size是分配内存的大小。
cudaError_t cudaFree(void * devPtr)
用于释放GPU上的内存。
13.用于拷贝主机和设备之间数据的函数是
cudaError_t cudaMemcpy(void * dst,const void * src,size_t count,cudaMemcpyKind kind)
此函数从src指向的存储空间复制一定字节数的数据到由dst指定的数据存储区。count表示复制的字节数量,kind表示复制的方向,有:
cudaMemcpyHostToDevice(主机到设备)
cudaMemcpyDeviceToHost(设备到主机)
cudaMemcpyDeviceToDevice(设备到设备)
cudaMemcpyHostToHost(主机到主机)
注意这个函数是以同步方式执行的,在复制还没完成的时候此时主机的程序是被阻塞的,当这个函数执行完成以后主机程序和核函数才会继续运行,除了内核启动之外的CUDA函数都会返回一个错误的枚举类型:cudaError_t,内存分配成功则返回:
cudaSuccess
否则返回:
cudaErrorMemoryAllocation
可以使用以下函数将错误代码转换为错误的可读信息:
char * cudaGetErrorString(cudaError_t error);
14.nvcc 封装了几种内部编译器,CUDA编译器允许通过命令行选项启动不同的工具完成编译操作。当存在一个完全在主机上运行的C语言文件时,也可以通过nvcc来进行编译:
nvcc -Xcompiler -std=c99 hello.c -o hello
-Xcompiler用于制定命令行选项是指向C编译器还是预处理器,-std=c99表示使用什么样的标准来对代码进行处理。
(线程管理)15.当核函数在主机端启动时,它的执行会被移动到设备端,此时设备中会产生大量的线程,并且每个线程都会完成由核函数指定的操作。CUDA明确了线程抽象的概念,
这是一个两层的线程层次结构: 由线程块和线程块网格构成,线程网格内部含有线程块,线程块内部含有线程。
16.由一个内核启动所产生的所产生的线程为同一个网格,同一个线程块中的所有线程共享同一个全局内存空间,一个网格内部包含多个线程块,一个线程块中包含着一组线程,线程块中的线程可以通过两种方式相互协作:
同步
共享内存
不同线程块的线程不能进行相互协作。
线程依靠两个坐标变量来进行区分索引
blockIdx(线程块在线程网格内的索引位置)
threadIdx(线程在线程块内的索引位置)
这些变量是一个核函数需要初始化的内置变量,当执行一个核函数时,CUDA运行时为每个线程分配坐标变量(blockIdx,threadIdx),你可以将部分数据分配给不同的线程。
blockIdx,threadIdx两个变量是基于uint3定义的CUDA内置向量类型,可以通过x,y,z三个字段来指定:
blockIdx.x
blockIdx.y
blockIdx.z
threadIdx.x
threadIdx.y
threadIdx.z
17.CUDA可以组织三维的线程网格和线程块,网格和块的维度由两个dim3变量决定
blockDim(线程块的维度,表示一个线程块中的线程的个数)
gridDim(线程格的维度,表示一个线程格中的线程个数)
当定义一个dim3变量时,其中的未定义的变量默认为1,dim3中的每个组件都可以通过x,y,z获得
blockDim.x
blockDim.y
blockDim.z
通常情况下,一个线程格会被组织成线程块的二维数组的形式,而线程块会被组织成为线程的三维数组形式
18. 在主机端,可以通过定义dim3类型的数据自定义一个线程格和线程块的维度,当执行核函数时,会自动生成相应的内置初始化网格和块。
19.定义线程块维度和线程网格维度:
首先定义一个较小的数据
int nElement = 6;
其次定义线程块维度
dim3 block(3);
定义线程格维度
dim3 grid((nElement + block.x – 1)/(block.x));(必须这么定义)
网格大小是块大小的倍数.
20.输出每个线程的threadIdx和blockIdx的源码:
#include <cuda_runtime.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
__global__ void print_Id(void){
printf("threadIdx:(%d,%d,%d),blockIdx:(%d,%d,%d),blockDim:(%d,%d,%d),gridDim:(%d,%d,%d)\n",threadIdx.x,threadIdx.y,threadIdx.z,blockIdx.x,blockIdx.y,blockIdx.z,blockDim.x,blockDim.y,blockDim.z,gridDim.x,gridDim.y,gridDim.z);
}
int main(){
int nElement = 6;
cudaSetDevice(0);
dim3 block(3);
dim3 grid((nElement + block.x - 1)/ block.x);
printf("Host : block: (%d,%d,%d),grid:
(%d,%d,%d)\n",block.x,block.y,block.z,grid.x,grid.y,grid.z);
print_Id<<< grid,block >>> ();
cudaDeviceReset();
return 0;
}
21.要确定块尺寸,通常需要考虑:
内核的性能特性
GPU资源的限制
22.CUDA内核调用是对C语言函数调用语句的延伸,<<<>>>运算符内是核函数的运行配置。
Kernel_name <<<grid,block>>>(argument list)
执行配置的第一个参数是网格维度,也就是线程块的个数,第二个参数是线程块维度,也就是一个线程块中线程的个数。通过制定这两个参数,可以进行以下配置:
内核中线程的数目
内核中使用的线程的布局
23.核函数调用结束后控制权立刻返回主机端,它的调用与主机线程之间是异步的,要实现核函数与主机端的同步,可使用
cudaError_t cudaDeviceSynchronize(void);
cudaMemcpy函数只有当核函数计算完成后才开始进行数据的传输
24.函数类型限定符:
__global__
函数执行时在设备端执行,调用时在主机端调用或者在计算能力大于3.0的设备端调用,这类函数要求返回类型要为void型。__device__ 函数在设备端执行,且只能在设备端调用
__host__ 函数在主机端调用并执行,一般情况下可以忽略不记
25.CUDA核函数的限制
只能访问设备内存(不认可)
必须具有void返回类型(不认可)
不支持可变数量的参数
不支持静态变量
显示异步行为
26.CUDA中实现对向量的相加操作源码:
__global__
void add_vec(float *A,float *B,float *C){
int
count = threadIdx.x;
C[count]
= A[count] + B[count];
}
27.验证核函数
除了调试工具外,还可以使用printf(argument
list)函数来进行验证,
或者在配置过程<<<1,1>>>模拟串行进行编译,同时这也有助于验证数值是否按位计算
28.两个数组相加源码:
#include<stdio.h>
#include<cuda_runtime.h>
voidCHECK(const
cudaError_t error){
if(error!=
cudaSuccess)
{
printf("error:%s",cudaGetErrorString(error));
exit(1);
}
}
__global__voidarray_add_GPU(float
*A,float
*B,float
*C,size_t
count){
int
index_count = blockIdx.x * blockDim.x + threadIdx.x;
if(index_count
< count)
{
C[index_count]
= A[index_count]+B[index_count];
printf("threadIdx:%d,result
is %lf\n",threadIdx.x,C[index_count]);
}
}
voidinitial_data(float
*A,size_t
num){
for
(int
i = 0; i< num; i++)
{
A[i]
= i;
}
}
__host__voidshow_data_host(float
*show_data,size_t
n){
for
(int
index_count = 0;index_count < n;index_count ++)
{
printf("Host
Show : %lf\n",show_data[index_count]);
}
}
intmain(){
printf("Starting..\n");
cudaSetDevice(0);
float
*A,*B,*C;
int
nBytes = sizeof(float)
* 32;
A
=(float
*) malloc(nBytes);
B
=(float
*) malloc(nBytes);
C
=(float
*) malloc(nBytes);
initial_data(A,32);
initial_data(B,32);
//int
nBytes = sizeof(float)
* 32;
float
*d_A,*d_B,*d_C;
CHECK(cudaMalloc((float
**) &d_A,nBytes));
printf("debug\n");
cudaMalloc((float
**) &d_B,nBytes);
cudaMalloc((float
**) &d_C,nBytes);
cudaMemcpy((float
*)d_A,(float
*)A,nBytes,cudaMemcpyHostToDevice);
cudaMemcpy((float
*)d_B,(float
*)B,nBytes,cudaMemcpyHostToDevice);
array_add_GPU<<<1,512>>>(d_A,d_B,d_C,32);
cudaDeviceSynchronize();
cudaMemcpy((float
*)C,(float
*)d_C,nBytes,cudaMemcpyDeviceToHost);
show_data_host(C,32);
cudaFree(d_C);
cudaFree(d_A);
cudaFree(d_B);
free(A);
free(B);
free(C);
cudaDeviceReset();
return
0;
}
29.如果使用了合适的块大小和网格大小来正确地组织线程,那么会对性能进行极大的优化。
*30.矩阵运算,传统的方法是对CUDA使用一个包含二维网格与二维块的布局来组织线程,还可以使用如下布局:
由二维线程块构成的二维网格
由一维线程块构成的一维网格
由一位线程块构成的二维网格
31.通常情况下,一个矩阵的存储方式通常是行优先的方式在全局内存中进行线性存储。
*32.对于一个矩阵来说通常需要三种方式索引:
线程和块索引
矩阵中给定点的坐标
全局限性内存中的偏移量
*33.首先,通过以下公式把块索引和线程索引映射到矩阵的坐标上
ix
= blockIdx.x * blockDim.x + threadIdx.x
iy
= blockIdx.y * blockDim.y + threadIdx.y
其次,将矩阵坐标映射到全局内存的索引上:
idx
= iy * nx + ix
35.使用二维网格和一维块进行矩阵求和操作,即一个块针对一个数据进行优化,那么此时
ix
= threadIdx.x + blockDim.x * blockIdx.x
iy
= blockIdx.y;
将矩阵坐标映射到全局内存的索引上:
idx
= iy * nx + ix;
通过上述的几个不同的核函数配置和线程块和网格的布局不同可以认为:
改变执行配置对内核性能会产生影响
传统的核函数不一定能够达到最优性能
对于一个核函数,可以进行多次尝试找到最优性能
36.对于核函数的调试,当发现核函数无法进入等意外出现的时候,可以使用
cudaError_t cudaGetLastError();
获得,将该函数放置在核函数运行的下方,函数将会返回CUDA中所产生的错误。
CUDA编程模型的更多相关文章
- CUDA编程模型之内存管理
CUDA编程模型假设系统是由一个主机和一个设备组成的,而且各自拥有独立的内存. 主机:CPU及其内存(主机内存),主机内存中的变量名以h_为前缀,主机代码按照ANSI C标准进行编写 设备:GPU及其 ...
- CUDA刷新器:CUDA编程模型
CUDA刷新器:CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA,CUDA刷新器,并行编程 这是CUDA更新系列的第四篇文章,它的目标是 ...
- CUDA学习笔记(一)——CUDA编程模型
转自:http://blog.sina.com.cn/s/blog_48b9e1f90100fm56.html CUDA的代码分成两部分,一部分在host(CPU)上运行,是普通的C代码:另一部分在d ...
- CUDA编程模型——组织并行线程3 (2D grid 1D block)
当使用一个包含一维块的二维网格时,每个线程都只关注一个数据元素并且网格的第二个维数等于ny,如下图所示: 这可以看作是含有二维块的二维网格的特殊情况,其中块儿的第二个维数是1.因此,从块儿和线程索引到 ...
- CUDA编程模型——组织并行线程2 (1D grid 1D block)
在”组织并行编程1“中,通过组织并行线程为”2D grid 2D block“对矩阵求和,在本文中通过组织为 1D grid 1D block进行矩阵求和.一维网格和一维线程块的结构如下图: 其中,n ...
- cuda编程基础
转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...
- CUDA编程之快速入门
CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架构.做图像视觉领域的同学多多少少都会接触到CUDA,毕竟要做性能速度优化,CUDA是个很重要 ...
- CUDA编程学习笔记1
CUDA编程模型是一个异构模型,需要CPU和GPU协同工作. host和device host和device是两个重要的概念 host指代CPU及其内存 device指代GPU及其内存 __globa ...
- CUDA编程之快速入门【转】
https://www.cnblogs.com/skyfsm/p/9673960.html CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架 ...
随机推荐
- Linux 系统调用sysconf
1.前言 当前计算机都是多核的,linux2.6提供了进程绑定cpu功能,将进程指定到某个core上执行,方便管理进程.linux提供了sysconf系统调用可以获取系统的cpu个数和可用的cpu个数 ...
- @RunWith注解作用
@RunWith就是一个运行器 @RunWith(JUnit4.class)就是指用JUnit4来运行 @RunWith(SpringJUnit4ClassRunner.class),让测试运行于Sp ...
- PHP 两数组循环组合
- js 检测输入内容是否为空(小程序,网站)
一.输入内容只要有空格就会返回true if(/\s*/.test(data)){ return true }else{ return false } 二.先替换掉空格,更利于赋值或者判断 1.需要判 ...
- PCB载流你必须知道的那些事儿
也许大家都知道铜箔走线宽度与电流大小有关,往往都是硬件工程师让你走多少就走多少,但作为Layout工程师你还是要知道你的铜皮走线取值都由什么因素决定?取值多大才能让你安心? PCB的载流能力取决与以下 ...
- JavaScript复制文本探究
JS复制文本基本分为两步-First: 选中需要复制的节点,及选区:Second: 执行document.execCommand('copy')命令复制 对于选区,属于HTMLInputElement ...
- ISP PIPLINE (十一) color correction
什么是color correction? 为什么要进行color correction? 转换后的色彩饱和度更加明显,更加符合人眼感官. 如何进行color correction? 下图是步骤: 第一 ...
- BZOJ.5397.circular(随机化 贪心)
BZOJ 感觉自己完全没做过环上选线段的问题(除了一个2-SAT),所以来具体写一写qwq. 基本完全抄自remoon的题解qwq... (下标从\(0\sim m-1\)) 拆环为链,对于原线段\( ...
- Big big world
Big big world I'm a big big girl 我是个大女孩 in a big big world 在这个大大的世界上 It's not a big big thing if you ...
- 如何删除github上的某个文件夹
在github上只能删除仓库,却无法删除文件夹或文件, 所以只能通过命令来解决 首先进入你的master文件夹下, Git Bash Here ,打开命令窗口 $ git –help 帮助命令 $ g ...