CUDA核函数

在GPU上调用的函数成为CUDA核函数(Kernel function),核函数会被GPU上的多个线程执行。每个线程都会执行核函数里的代码,当然由于线程编号的不同,执行的代码路径可能会有所不同。下面的几行代码是向量加计算的CUDA核函数:

__global__ void vectoradd (int *a, int *b, int *c, int n){

int i = blockDim.x * blockIdx.x + threadIdx.x;

if (i < n){

c[i] = a[i] + b[i];

}

}

vectoradd<<>>(d_a, d_b, d_c, N);

从这段代码可以看出CUDA核函数的几个特征:

-函数的最前面是声明标识符__global__,该标识符表示这个函数可以在GPU上执行。需要指出的是尽管是在GPU上执行,但是仍然是由CPU端发起调用的

-核函数调用时需要用<<<...>>>符号来指定线程配置

-在核函数内部可以调用CUDA内置变量,比如threadIdx,blockDim等

-核函数相对于CPU代码是异步的,也就是控制会在核函数执行完成之前就返回,这样CPU就可以不用等待核函数的完成而继续执行后面的CPU代码

关于线程的配置以及内置变量将会在后面详细介绍。CUDA核函数除了上面提到的几个特征之外,还有一些限制:

-核函数内部只能访问device内存。因为核函数是执行在设备端,所以只能访问设备端内存。

-必须返回void类型。我们知道核函数是由CPU端发起的并执行在GPU上的函数。在核函数内部的数据均是位于GPU上的,假设核函数有返回值,那么返回值是位于GPU上的数据,CPU去直接接收这个数据是不被允许的。所以,核函数没有返回值。

-核函数不支持可变参数

-核函数不支持静态变量

-核函数不支持函数指针

在CUDA编程中,除了__global__外,常用的标识符还有:

__device__

-有标识符__device__的函数只能在GPU段执行

-只能在GPU段调用,比如可以在__global__以及__device__函数中调用

-__global__与__device__不能同时使用

另外一个常用的标识符是__host__:

-只能在host端执行

-只能在host端调用

单独使用__host__的情况时,该函数与普通的CPU函数的性质及使用方法没有任何差别。那既然这样为什么还要引入这个标识符呢?我们可以想象有这样一种情况,一个函数我们希望它既可以在CPU上调用也可以在GPU上调用,那么我们这样声明这个函数:__host__ __device__ funForCPUandGPU(args), 则这个函数既可以在CPU上执行也可以在GPU上执行。

线程配置

前面提到,在调用核函数时需要通过<<<...>>>指定线程配置,在具体介绍之前,我们先来了解CUDA编程中几个基本的概念。

线程(Thread)是CUDA程序的基本执行单元,每个线程内的执行都会顺序执行。所有的线程都会执行相同的代码,当然有可能会执行相同代码的不同分支。所有的线程之间是并行执行的,没有先后之分。

线程块(Thread Block)是由一组线程组成。每个线程块内部的线程之间可以进行协作,有可以共同访问的内存-共享内存。每个线程块会在GPU上的某一个流处理器(Streaming Multiprocessor, SM)中执行。

线程网格(Thread Grid)是一组线程块的集合。线程网格里的线程块会被调度到GPU的多个SM上去执行。线程块之间并没有同步机制,线程块被执行的先后顺序是不确定的。线程块之间的通讯比较昂贵,需要通过全局内存(global memory)来实现。

CUDA线程层次。每个核函数对应一个线程网格,每一个线程网格包含多个线程块,每个线程块包含多个线程。线程网格以及线程块可以是1维、2维或者3维的。

在调用核函数时需要指定的线程配置就是需要给定每个线程网格中有多少线程块,每个线程块有多少线程,并且他们的排列方式是怎样的。一个线程配置的例子如下:

dim3 grid(3,2,1), block(5,3,1)

kernel_name<<>>(…)

线程网格以及线程块的数据类型是dim3,实质上是一个结构体,有三个变量分别用来描述x、y、z三个方向的长度。<<<...>>>中的第一个参数用来指定线程网格的结构,也就是每个线程网格中有多少线程块,上面的例子中每个线程网格中有321=6个线程块,排布方式是三个方向上分别是3、2、1。第二个参数是用来指定线程块的结构,也就是每个线程块中有多少个线程,上面的例子中每个线程块中有531=15个线程,排布方式是三个方向上分别是5、3、1。<<<...>>>也可以接受整型变量,比如<<<6, 32>>>代表一个线程网格中有6个线程块,一维排布,一个线程网格块内有32个线程,同样一维排布。这样整个核函数内的总的线程数就是6*32=192。

另外核函数内部可以使用CUDA的内置变量来获取线程号以及线程块号:

threadIdx.[x y z]指的是线程块内线程的编号

blockIdx.[x y z]指的是线程网格内线程块的编号

blockDim.[x y z]指的是线程块的维度,也就是线程块中每个方向上线程的数目

gridDim.[x y z]指的是线程网格的维度,也就是线程网格中每个方向上线程块的数目

下面我们来看一个简单的例子,线程网格有4个线程块,每个线程块内有8个线程,并且都是一维排布:

kernel_name<<<4, 8>>> (argument list)

具体的线程配置以及相应的内置变量的值如下图所示

内置变量均从0开始编号

从上图可以看出,我们可以很轻易的获取一个线程在线程块的位置。在核函数中,我们经常需要得知一个线程在一个线程网格中的位置,那么该怎么计算呢?同样来看一个简单的例子:

dim3 grid(4,1,1), block(4,1,1)

上面的例子中有4个线程块,每个线程块中4个线程,假设我们需要计算红色标记的线程在线程网格中的位置。观察上图,我们可以分成两个部分进程计算,首先计算该线程所在线程块前面总共有多少线程,然后在加上该线程在当前线程块的位置就可以获取在整个线程网格中的位置。该线程所在的线程块编号是blockIdx.x,每个线程块内的线程数是blockDim.x,那总的线程数是blockIdx.x * blockDim.x. 再加上该线程在当前线程块中的位置threadIdx.x,则有:

int idx = blockIdx.x * blockDim.x + threadIdx.x;

二维与三维的情况会稍微复杂些,但计算方法是一样的。下面是一段打印二维线程编号的核函数的例子,自己可以尝试编译运行,相信会有助于对线程位置的计算加深理解。

#include

#include

__global__ void printThreadIndex() {

int ix = threadIdx.x + blockIdx.x * blockDim.x;

int iy = threadIdx.y + blockIdx.y * blockDim.y;

unsigned int idx = iy*blockDim.x * gridDim.x + ix;

printf("thread_id (%d,%d) block_id (%d,%d) coordinate (%d, %d), global index %2d \n",

threadIdx.x, threadIdx.y, blockIdx.x, blockIdx.y, ix, iy, idx);

}

int main(void) {

dim3 grid(2, 3, 1), block(4, 8, 1);

printThreadIndex<<>>();

cudaResetDevice();

return 0;

}

c语言 cuda核函数,CUDA核函数与线程配置相关推荐

  1. cuda学习(3):核函数

    1. 核函数介绍 核函数是cuda编程的关键 通过xxx.cu创建一个cudac程序文件,并把cu交给nvcc编译(nvcc 是nvidia的c++编译器,编译cudac程序,是c++的超集),才能识 ...

  2. CUDA性能优化----线程配置

    CUDA性能优化----线程配置 2017-01-12 14:19:29|  分类: HPC&CUDA优化 |  标签:cuda  gpu  hpc   |举报 |字号 订阅 下载LOFTER ...

  3. 最优的cuda线程配置

    最优的cuda线程配置 1 每个SM上面失少要有192个激活线程,寄存器写后读的数据依赖才能被掩盖   2 将 寄存器 的bank冲突降到最低,应尽量使每个block含有的线程数是64的倍数   3 ...

  4. VS2017 CUDA编程学习8:线程同步-原子操作

    文章目录 前言 1. 原子操作的理解 2. C++ CUDA实现原子操作 3. 执行结果 总结 学习资料 VS2017 CUDA编程学习1:CUDA编程两变量加法运算 VS2017 CUDA编程学习2 ...

  5. 显卡、显卡驱动、CUDA、CUDA Toolkit、cuDNN 梳理

    显卡.显卡驱动.CUDA.CUDA Toolkit.cuDNN 梳理 转自:https://www.cnblogs.com/marsggbo/p/11838823.html#nvccnvidia-sm ...

  6. 显卡,GPU,显卡驱动,CUDA ,CUDA Toolkit之间的关系

    相关知识收集于网络,主要来自 显卡,显卡驱动,nvcc, cuda driver,cudatoolkit,cudnn到底是什么? GPU 和显卡是什么关系? 显卡.显卡驱动.cuda 之间的关系是什么 ...

  7. 一篇文章清晰了解NVIDAI显卡驱动(包括:CUDA、CUDA Driver、CUDA Toolkit、CUDNN、NCVV)

    背景 开发过程中需要用到GPU时,通常在安装配置GPU的环境过程中遇到问题:比如:安装TensorFlow2.1过程中,想要使用到电脑的显卡来进行开发,但是发现默认需要CUDATOOLKIT=10.1 ...

  8. 了解NVIDIA显卡驱动(包括CUDA、CUDA Driver、CUDA Toolkit、CUDNN、NCVV)

    背景 开发过程中需要用到GPU时,通常在安装配置GPU的环境过程中遇到问题:CUDA Toolkit和CUDNN版本的对应关系:CUDA和电脑显卡驱动的版本的对应关系:CUDA Toolkit.CUD ...

  9. 了解NVIDAI显卡驱动(包括:CUDA、CUDA Driver、CUDA Toolkit、CUDNN、NCVV)

    转载 一篇 背景 开发过程中需要用到GPU时,通常在安装配置GPU的环境过程中遇到问题:CUDA Toolkit和CUDNN版本的对应关系:CUDA和电脑显卡驱动的版本的对应关系:CUDA Toolk ...

最新文章

  1. mysql本地可以访问 网络不能访问
  2. 面试和学习必备--Java多线程
  3. 【阿里云课程】如何从零开始完成一个完整的CV项目
  4. 使用 jquery 创建数组
  5. Web安全——正方教务系统自主选课非正常退课解决方案(危险操作,仅用于学习)
  6. sql server 2008学习13 触发器
  7. elasticsearch6.2.4 与logstash与kibana版本6.2.4搭建同步使用
  8. js数组的拷贝赋值复制二三事总结
  9. input文字垂直居中_CSS的带文字居中分析
  10. [USACO09JAN]安全出行Safe Travel
  11. 条件随机场Conditional Random Field,CRF、隐马尔可夫模型Hidden Markov Model,HMM、马尔可夫随机场、马尔可夫性质傻傻分不清楚?帮你理理关系
  12. 如何判断一个正数是奇数还是偶数,奇数输出odd,偶数输出even
  13. 前后端分离实现上传图片的功能
  14. 电脑开机正常,但显示器没反应怎么办
  15. 抛弃Visio,遇上效率作图工具Edraw亿图图示
  16. 博士补贴75万、本硕补贴45万!小县城重金揽才,开启硕博抢人大战!
  17. 离散数学·通路与回路、图的连通性、连通度
  18. 9.27 股票盈利计划 只做超短线 利益最大化
  19. 常用搜索引擎搜索串【ZT+原创】
  20. Windows截取gif动态图的软件 ScreenToGif 的安装、使用教程

热门文章

  1. 那些年,面过的奇葩面试(java)
  2. 廊坊师范学院吧网络舆情分析报告
  3. Mysql-1366 - Incorrect string value: ‘\xE5\xBC\xA0\xE4\xB8\x89‘ for column ‘userName‘ at row 1
  4. 我曾经七次鄙视自己的灵魂【卡里·纪伯伦】
  5. IPv4地址分类(A类 B类 C类 D类 E类)
  6. java窗口的repaint_Java Swing revalidate()与repaint()
  7. 安兰德写作竞赛可以获得多少奖金?
  8. MyEclipse快捷键大全(Z)
  9. 2020年程序员日,是个特别的日子
  10. 亚马逊云科技re:Invent现场访谈:定制芯片将是大势所趋