Copyright © 1900-2016, NORYES, All Rights Reserved.

http://www.cnblogs.com/noryes/

欢迎转载,请保留此版权声明。

-----------------------------------------------------------------------------------------

转载自http://blog.csdn.net/abcjennifer/article/details/42436727

本文从软硬件层面讲一下CUDA的结构,应用,逻辑和接口。分为以下章节:

(一)、GPU与CPU

(二)、CUDA硬件层面

(三)、CUDA安装

(四)、CUDA 结构与接口

4.1 Kernels

4.2 Thread,Block, Grid

4.3 Memory

4.4 Execution

(五)、码HelloWorld——数组求和

(一)、GPU与CPU

对于浮点数操作能力,CPU与GPU的能力相差在GPU更适用于计算强度高,多并行的计算中。因此,GPU拥有更多晶体管,而不是像CPU一样的数据Cache和流程控制器。这样的设计是因为多并行计算的时候每个数据单元执行相同程序,不需要那么繁琐的流程控制,而更需要高计算能力,这也不需要大cache。

(二)、CUDA硬件层面:

Nvidia于2006年引入CUDA,一个GPU内嵌通用并行计算平台。CUDA支持C, C++, Fortran, Java, Python等语言。

那么一个多线程CUDA程序如何执行的呢?

GPU建立在一组多处理器(SMX,Streaming Multiprocessors)附近。

一个SMX的配置:

  • 192 cores(都是SIMT cores(Single Instruction Multiple Threads) and 64k registers(如下图所示)

GPU中的SIMT对应于CPU中的SIMD(Single Instruction Multiple Data)

  • 64KB of shared memory / L1 cache
  • 8KB cache for constants
  • 48KB texture cache for read-only arrays
  • up to 2K threads per SMX

不同显卡有不同配置(即SMX数量不同),几个例子:

每个multi-thread程序的execution kernel instance(kernel定义见下一节,instance指block)在一个SMX上执行,一个多线程程序会分配到blocks of threads(每个block中负责一部分线程)中独立执行。所以GPU中的处理器越多执行越快(因为如果SMX不够给每个kernel
instance分配一个,就要几个kernel抢一个SMX了)。具体来讲,如果SMX上有足够寄存器和内存(后面会讲到,shared memory),就多个kernel instance在一个SMX上执行,否则放到队列里等。

图:表示不同SM数带来的执行速度差异。

GPU工作原理:首先通过主接口读取中央处理器指令,GigaThread引擎从系统内存中获取特定的数据并拷贝到显存中,为显存控制器提供数据存取所需的高带宽。GigaThread引擎随后为各个SMX创建和分派线程块(warp, 详细介绍见SIMT架构或者CUDA系列学习(二)),SMX则将多个Warp调度到各CUDA核心以及其他执行单元。在图形流水线出现工作超载的时候,GigaThread引擎还负责进行工作的重新分配。

(三)、CUDA安装

装CUDA主要装以下3个组建:

1. driver

  • low-level software that controls the graphics card

2. toolkit

  • nvcc CUDA compiler
  • profiling and debugging tools
  • several libraries

3. SDK

  • lots of demonstration examples
  • some error-checking utilities
  • not officially supported by NVIDIA
  • almost no documentation

详情请见CUDA
安装与配置

(四)、CUDA 结构与接口

4.1 Kernels

CUDA C 中可通过定义kernel,每次被调用就在N个CUDA thread中并行执行。

kernel的定义:

  • 声明 __global__
  • 配置kernel_routine<<<gridDim, Blockdim>>>(args)

其中gridDim和Blockdim变量可以是int或dim3(<=3维)类型的变量。gridDim表示每个grid中block结构(the number of instances(blocks) of
the kernel),Blockdim表示每个block中thread结构。那么。。thread,block,grid又是啥?往下看。。。见4.2节

  • 每个执行该kernel的thread都会通过被分配到一个unique thread ID,就是built-in变量:threadIdx

4.2 Thread,Block,Grid

很多threads组成1维,2维or3维的thread block. 为了标记thread在block中的位置(index),我们可以用上面讲的threadIdx。threadIdx是一个维度<=3的vector。还可以用thread index(一个标量)表示这个位置。

thread的index与threadIdx的关系:

Thread index
1 T
2 T.x + T.y * Dx
3 T.x+T.y*Dx+z*Dx*Dy

其中T表示变量threadIdx。(Dx, Dy, Dz)为block的size(每一维有多少threads)。

因为一个block内的所有threads会在同一处理器内核上共享内存资源,所以block内有多少threads是有限制的。目前GPU限制每个 block最多有1024个threads。但是一个kernel可以在多个相同shape的block上执行,效果等效于在一个有N*#thread per block个thread的block上执行。

Block又被组织成grid。同样,grid中block也可以被组织成1维,2维or3维。一个grid中的block数量由系统中处理器个数或待处理的数据量决定。

和threadIdx类似,对于block有built-in变量blockDim(block dimension)和blockIdx(block index)。

回过头来看4.1中的configureation,举个栗子,假设A,B,C都是大小[N][N]的二维矩阵,kernel MatAdd目的将A,B对应位置元素加和给C的对应位置。

声明:

[cpp] view
plain
 copy

 

  1. // Kernel definition
  2. __global__ void MatAdd(float A[N][N], float B[N][N],
  3. float C[N][N])
  4. {
  5. int i = blockIdx.x * blockDim.x + threadIdx.x;
  6. int j = blockIdx.y * blockDim.y + threadIdx.y;
  7. if (i < N && j < N)
  8. C[i][j] = A[i][j] + B[i][j];
  9. }
  10. int main()
  11. {
  12. ...
  13. // Kernel invocation
  14. dim3 threadsPerBlock(16, 16);
  15. dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
  16. MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
  17. ...
  18. }

这里threadsPerBlock(16,16)一般是标配。例子中,我们假定grid中block足够多,保证N/threadsPerBlock不会超限。

4.3 Memory

前面提到了Block中的threads共享内存,那么怎样同步呢?在kernel中调用内部__synthreads()函数,其作用是block内的所有threads必须全部执行完,程序才能继续往下走。那么thread到底怎样使用memory呢?

  • 每个thread有private local memory
  • 每个block有shared memory
  • 所有thread都能访问到相同的一块global memory
  • 所有thread都能访问两块read-only memory:constant & texture array(通常放查找表)

其中,global,constant,texture memory伴随kernel生死。

CUDA程序执行的时候,GPU就像一个独立的设备一样,kernel部分由GPU执行,其余部分CPU执行。于是memory就被分为host memory(for CPU)& device memory(for GPU)。因此,一个程序需要在CUDA运行时管理device memory的分配,释放和device & host memory之间的data transfer。

4.4 Execution

从执行角度看,程序经过了以下步骤:

1. initialises card
2. allocates memory in host and on device
3. copies data from host to device memory
4. launches multiple instances of execution “kernel” on device
5. copies data from device memory to host
6. repeats 3-5 as needed
7. de-allocates all memory and terminates

总结:每个kernel放在一个grid上执行,1个kernel有多个instance,每个instance在一个block上执行,每个block只能在一个SM上执行,如果block数>SM数,多个block抢SM用。kernel的一个instance在SMX上通过一组进程来执行。如下图所示:

总结:

CUDA的3个key abstraction:thread groups, shared memories, 和barrier synchronization

CUDA中的built-in变量:gridDim, blockDim, blockIdx(block在grid中的index), threadIdx, warpSize(threads的warp size)

(五)、码HelloWorld

  • kernel code很像MPI,从单线程的角度coding
  • 需要think about每个变量放在哪块内存

这里我们以数组对应元素相加为例,看下Code :

[cpp] view
plain
 copy

 

  1. #include<cutil_inline.h>
  2. #include<iostream>
  3. using namespace std;
  4. #define N 32
  5. // Kernel definition
  6. __global__ void MatAdd(float A[N], float B[N], float* C)
  7. {
  8. int i = blockIdx.x * blockDim.x + threadIdx.x; //get thread index by built-in variables
  9. if (i < N)
  10. C[i] = A[i] + B[i];
  11. }
  12. int main()
  13. {
  14. float A[N],B[N]; // host variable
  15. float *dA, *dB; // device variable, to have same value with A,B
  16. float *device_res, *host_res; // device and host result, to be device and host variable respectively
  17. // initialize host variable
  18. memset(A,0,sizeof(A));
  19. memset(B,0,sizeof(B));
  20. A[0] = 1;
  21. B[0] = 2;
  22. // allocate for device variable and set value to them
  23. cudaMalloc((void**) &dA,N*sizeof(float));
  24. cudaMalloc((void**) &dB,N*sizeof(float));
  25. cudaMemcpy(dA, A, N*sizeof(float),cudaMemcpyHostToDevice);
  26. cudaMemcpy(dB, B, N*sizeof(float),cudaMemcpyHostToDevice);
  27. //malloc for host and device variable
  28. host_res = (float*) malloc(N*sizeof(float));
  29. cudaMalloc((void**)&device_res, N*sizeof(float));
  30. // Kernel invocation
  31. int threadsPerBlock = 16;
  32. int numBlocks = N/threadsPerBlock;
  33. MatAdd<<<numBlocks, threadsPerBlock>>>(dA, dB, device_res);
  34. cudaMemcpy(host_res, device_res, N*sizeof(float),cudaMemcpyDeviceToHost); //copy from device to host
  35. // validate
  36. int i;
  37. float sum = 0;
  38. for(i=0;i<N;i++)
  39. sum += host_res[i];
  40. cout<<sum<<endl;
  41. //free variables
  42. cudaFree(dA);
  43. cudaFree(dB);
  44. cudaFree(device_res);
  45. free(host_res);
  46. }

编译:

nvcc -I ~/NVIDIA_GPU_Computing_SDK/C/common/inc/ Matadd.cu 
运行结果:

3

OK,大功告成。

这里注意kernel部分的code,所有变量都必须是device variable,即需要通过cudaMalloc分配过memory的。之前我忘记将A,B数组cudaMemcpy到dA,dB,而直接传入MatAdd kernel就出现了运行一次过后卡住的问题。

参考:

1. CUDA C Programming Guide

2. An Introduction to CUDA

3. CUDA 安装与配置

4. CUDA调试工具——CUDA
GDB

5. GPU工作方式

6. Fermi
架构白皮书
(GPU继承了Fermi的很多架构特点)

7. GTX460架构

计算机系列:CUDA 深入研究的更多相关文章

  1. c++学习书籍推荐《清华大学计算机系列教材:数据结构(C++语言版)(第3版)》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <清华大学计算机系列教材:数据结构(C++语言版)(第3版)>习题解析涵盖验证型.拓展型.反思型.实践型和研究型习题,总计290余道大题.525道 ...

  2. C++ 系列:设计模式研究

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  3. 线程系列3---ThreadLocal类研究

    2013-12-23 17:44:44 Java为线程安全提供了一些工具类,如ThreadLocal类,它代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副 ...

  4. spring源码分析系列4:ApplicationContext研究

    ApplicationContext接口 首先看一下一个最基本的上下文应该是什么样子 ApplicationContext接口的注释里写的很清楚: 一个基本applicationContext应该提供 ...

  5. 计算机体系结构:量化研究方法(中文第五版)_扫描版_23.5M.pdf

  6. 人工智能中小样本问题相关的系列模型演变及学习笔记(二):生成对抗网络 GAN

    [说在前面]本人博客新手一枚,象牙塔的老白,职业场的小白.以下内容仅为个人见解,欢迎批评指正,不喜勿喷![握手][握手] [再啰嗦一下]本文衔接上一个随笔:人工智能中小样本问题相关的系列模型演变及学习 ...

  7. dedecms代码研究一

    dedecms相信大家一定都知道这个cms系统,功能比较强大,有比较完善的内容发布,还有内容静态化系统,还有就是它有自己独特的标签系统和模板系统.而模板系统也是其他cms系统比较难模仿的的东西,这个东 ...

  8. SIGGRAPH 2017:深度学习与计算机图形学的碰撞

    每年由美国计算机协会(Association of Computing Machinery,简称ACM)计算机图形专业组举办的年会SIGGRAPH,是全球最负盛名的图形学和交互技术盛会.今年已经是这场 ...

  9. CUDA笔记(七)

    今天集中时间找程序的问题.于是发现: 首先,程序里的kernel想要调试,必须用nsight. 于是一堆找.http://www.nvidia.com/object/nsight.html http: ...

随机推荐

  1. C#-WebForm-WebForm开发基础

    1.C/S 客户端应用程序 WinForm WPF 平级 数据是存放在其他的电脑上或服务器上 需要从服务器上下载相应的数据,在本地电脑上的客户端里进行加工 数据的加工是在用户的电脑上执行的,会对用户的 ...

  2. Interface小例子

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  3. strlen()和sizeof()求数组长度

    在字符常量和字符串常量的博文里有提: 求字符串数组的长度 标准库函数strlen(s)可以返回字符串s的长度,在头文件<string.h>里. strlen(s)的判断长度的依据是(s[i ...

  4. 【总结】详细说说@Html.ActionLink()的用法

    一.@Html.ActionLink()概述 在MVC的Rasor视图引擎中,微软采用一种全新的方式来表示从前的超链接方式,它代替了从前的繁杂的超链接标签,让代码看起来更加简洁,通过浏览器依然会解析成 ...

  5. MVC前后端数据被编码

    @{ ViewBag.Title = "Home Page";}<script> function htmldecode(s) { console.log(s); va ...

  6. CSS3中的动画功能(一)

    css3中的动画功能分为transitions功能和animations功能,这两种功能都可以通过改变css属性值来产生动画效果.今天带大家一起来看看css3动画功能中的transitions的用法. ...

  7. centos 7.0 编译安装php 5.6.7

    编译安装php参考资料 MySQL PHP API http://dev.mysql.com/doc/apis-php/en/index.html nginx + php +mysql 最简单安装 官 ...

  8. 【转】Controllers and Routers in ASP.NET MVC 3

    Controllers and Routers in ASP.NET MVC 3 ambilykk, 3 May 2011 CPOL 4.79 (23 votes) Rate: vote 1vote ...

  9. rqnoj378 约会计划

    题目描述 cc是个超级帅哥,口才又好,rp极高(这句话似乎降rp),又非常的幽默,所以很多mm都跟他关系不错.然而,最关键的是,cc能够很好的调解各各妹妹间的关系.mm之间的关系及其复杂,cc必须严格 ...

  10. UIScrollView无法滚动的解决办法

    如果UIScrollView无法滚动,可能是以下原因: 没有设置contentSize scrollEnabled = NO 没有接收到触摸事件:userInteractionEnabled = NO ...