CUDA系列学习(一)An Introduction to GPU and CUDA

本文从软硬件层面讲一下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(如下图所示)
  • 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数带来的执行速度差异。

(三)、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的对应位置。

声明:

// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
float C[N][N])
{
	int i = blockIdx.x * blockDim.x + threadIdx.x;
	int j = blockIdx.y * blockDim.y + threadIdx.y;
	if (i < N && j < N)
	C[i][j] = A[i][j] + B[i][j];
}
int main()
{
	...
	// Kernel invocation
	dim3 threadsPerBlock(16, 16);
	dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
	MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
	...
}

这里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, threadIdx

(五)、码HelloWorld

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

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

#include<cutil_inline.h>
#include<iostream>
using namespace std;

#define N 32

// Kernel definition
__global__ void MatAdd(float A[N], float B[N], float* C)
{
	int i = blockIdx.x * blockDim.x + threadIdx.x; //get thread index by built-in variables
	if (i < N)
		C[i] = A[i] + B[i];
}		

int main()
{
	float A[N],B[N]; // host variable
	float *dA, *dB; // device variable, to have same value with A,B
	float *device_res, *host_res; // device and host result, to be device and host variable respectively

	// initialize host variable
	memset(A,0,sizeof(A));
	memset(B,0,sizeof(B));
	A[0] = 1;
	B[0] = 2;

	// allocate for device variable and set value to them
	cudaMalloc((void**) &dA,N*sizeof(float));
	cudaMalloc((void**) &dB,N*sizeof(float));
	cudaMemcpy(dA, A, N*sizeof(float),cudaMemcpyHostToDevice);
	cudaMemcpy(dB, B, N*sizeof(float),cudaMemcpyHostToDevice);

	//malloc for host and device variable
	host_res = (float*) malloc(N*sizeof(float));
	cudaMalloc((void**)&device_res, N*sizeof(float));

	// Kernel invocation
	int threadsPerBlock = 16;
	int numBlocks = N/threadsPerBlock;
	MatAdd<<<numBlocks, threadsPerBlock>>>(dA, dB, device_res);

	cudaMemcpy(host_res, device_res, N*sizeof(float),cudaMemcpyDeviceToHost); //copy from device to host

	// validate
	int i;
	float sum = 0;
	for(i=0;i<N;i++)
		sum += host_res[i];
	cout<<sum<<endl;

	//free variables
	cudaFree(dA);
	cudaFree(dB);

	cudaFree(device_res);
	free(host_res);
}

编译:

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

最后,帮我投个票呗~

时间: 2024-11-10 12:08:31

CUDA系列学习(一)An Introduction to GPU and CUDA的相关文章

CUDA系列学习(三)GPU设计与结构QA &amp; coding练习

啥?你把CUDA系列学习(一),(二)都看完了还不知道為什麼要用GPU提速? 是啊..经微博上的反馈我默默感觉到提出这样问题的小伙伴不在少数,但是更多小伙伴应该是看了(一)就感觉离自己太远所以赶紧撤粉跑掉了...都怪我没有写CUDA系列学习(零)...那么,这一章就补上这一块,通过一堆Q&A进行讲解,并辅助coding练习,希望大家感觉贴近CUDA是这么容易~~ 请注意各个Q&A之间有顺序关系,轻依次阅读~ 否则不容易懂喔~ Q:现在硬件层面通常通过什么样的方法加速?A: - More p

CUDA系列学习(二)CUDA memory &amp; variables

本文来介绍CUDA的memory和变量存放,分为以下章节: (一).CPU Memory 结构 (二).GPU Memory结构 (三).CUDA Context (四).kernel设计 (五).变量 & Memory 5.1 global arrays 5.2 global variables 5.3 Constant variables 5.4 Register 5.5 Local Array 5.6 Shared Memory 5.7 Texture Memory 5.8 总结 (一).

把书《CUDA By Example an Introduction to General Purpose GPU Programming》读薄

鉴于自己的毕设需要使用GPU CUDA这项技术,想找一本入门的教材,选择了Jason Sanders等所著的书<CUDA By Example an Introduction to General Purpose GPU Programming>.这本书作为入门教材,写的很不错.自己觉得从理解与记忆的角度的出发,书中很多内容都可以被省略掉,于是就有了这篇博文.此博文记录与总结此书的笔记和理解.注意本文并没有按照书中章节的顺序来写.书中第8章图像互操作性和第11章多GPU系统上的CUDA C,这

CUDA系列学习(五)GPU基础算法: Reduce, Scan, Histogram

喵~不知不觉到了CUDA系列学习第五讲,前几讲中我们主要介绍了基础GPU中的软硬件结构,内存管理,task类型等:这一讲中我们将介绍3个基础的GPU算法:reduce,scan,histogram,它们在并行算法中非常常用,我们在本文中分别就其功能用处,串行与并行实现进行阐述. 1. Task complexity task complexity包括step complexity(可以并行成几个操作) & work complexity(总共有多少个工作要做). e.g. 下面的tree-str

win10 用cmake 3.5.2 和 vs 2015 update1 编译 GPU版本(cuda 8.0, cudnn v5 for cuda 8.0)

win10 用cmake 3.5.2 和 vs 2015 update1 编译 GPU版本(cuda 8.0, cudnn v5 for cuda 8.0)  用vs 2015打开 编译Release和Debug版本 看网上那个例子里面 工程里面有是三个文件夹 include(包含mxnet,dmlc,mshadow的include目录) lib(包含libmxnet.dll, libmxnet.lib,把用vs编译好的放过去) python(包含一个mxnet,setup.py, 以及buil

Ubuntu12.04 之 CUDA 编程 (二) ~~~ GPU 程序加速

关于 Ubuntu12.04 下 CUDA5.5 的安装请参看如下链接 Ubuntu-12.04 安装 CUDA-5.5 关于 Ubuntu12.04 下 CUDA5.5 程序的运行请参看如下链接 Ubuntu12.04 之 CUDA 编程 (一) --- GPU 运行程序 1.程序的并行化 前一篇文章讲到了如何利用 CUDA5.5 在 GPU 中运行一个程序.通过程序的运行,我们看到了 GPU 确实可以作为一个运算器,但是,我们在前面的例子中并没有正真的发挥 GPU 并行处理程序的能力,也就是

Ubuntu16.04 安装配置 Caffe 过程 (GPU版+CUDA 9.0+cuDNN 9.0+OpenCV 3.4.1)

虽然 Caffe 的官网已经有比较详细的针对 Ubuntu 的安装教程,但是要配置可以使用 GPU 的 Caffe 需要的依赖太多,包括 CUDA,cuDNN,OpenCV 等.参考了网上的很多教程,但在自己的配置中依旧出现了各种各样的意想不到的坑,所以在此记录一下自己配置 Caffe 的过程,以供参考.因为是配置完成后以回忆的形式做的记录,所以可能会有细节上的遗漏,还请见谅. 安装 Nvidia 驱动 1. 查询 NVIDIA 显卡驱动 去官网查询自己的显卡对应的驱动 http://www.n

Install Tensorflow GPU with CUDA 10.1 for python on Windows

How to install Tensorflow GPU with CUDA 10.0 for python on Windows 在cuda 10.0的windows上安装Tensorflow GPU, python ref: https://www.pytorials.com/how-to-install-tensorflow-gpu-with-cuda-10-0-for-python-on-windows/ Before start, Notations neet to know: Ta

CUDA C 编程指导(二):CUDA编程模型详解

CUDA编程模型详解 本文以vectorAdd为例,通过描述C在CUDA中的使用(vectorAdd这个例子可以在CUDA sample中找到.)来介绍CUDA编程模型的主要概念.CUDA C的进一步描述可以参考<Programming Interface>. 主要内容包括: 1.Kernels(核函数) 2.Thread Hierarchy(线程结构) 3.Memory Hierarchy(存储结构) 4.Heterogeneous Programming(异构编程) 5.Compute C