GPU & CPU编程

GPU除了用处图形渲染领域外,还可以用来做大规模的并行运算,这里我们称其为GPGPU(General Purpose GPU);GPGPU计算通常采用CPU+GPU异构模式,由CPU负责执行复杂逻辑处理和事务管理等不适合数据并行的计算,由GPU负责计算密集型的大规模并行计算。比如医学上对图像进行重建、解大规模方程组等,接下来让我们进入GPU高性能运算之CUDA的世界吧!

CUDA编程:

CUDA编程中,习惯称CPU为Host,GPU为Device。Grid、Block和Thread的关系

Kernel :在GPU上执行的程序,一个Kernel对应一个Grid。

Grid    
:一组Block,有共享全局内存

Block   :由相互合作的一组线程组成。一个block中的thread可以彼此同步,快速交换数据,最多可以同时512个线程。

Thread  :并行运算的基本单位(轻量级的线程)

其结构如下图所示:

?


1

2

3

4

5

6

7

8

9

10

/*

另外:Block和Thread都有各自的ID,记作blockIdx(1D,2D),threadIdx(1D,2D,3D)

Block和Thread还有Dim,即blockDim与threadDim.
他们都有三个分量x,y,z

线程同步:void
__syncthreads(); 可以同步一个Block内的所有线程

总结来说,每个
thread 都有自己的一份 register 和 local memory 的空间。

一组thread构成一个
block,这些 thread 则共享有一份shared memory。

此外,所有的
thread(包括不同 block 的 thread)都共享一份

global
memory、constant memory、和 texture memory。

不同的
grid 则有各自的 global memory、constant memory 和 texture memory。

*/

?

存储层次

1

2

3

4

5

6

7

per-thread

register
                            
1 cycle

per-thread

local memory                     slow

per-block
shared memory                   1 cycle

per-grid
global memory                       500 cycle,not cached!!

constant
and texture memories            500 cycle, but cached and read-only

分配内存:cudaMalloc,cudaFree,它们分配的是global
memory

Hose-Device数据交换:cudaMemcpy

?

变量类型

1

2

3

4

5

__device__  
//
GPU的global memory空间,grid中所有线程可访问

__constant__
//
GPU的constant memory空间,grid中所有线程可访问

__shared__  
//
GPU上的thread block空间,block中所有线程可访问

local       
//
位于SM内,仅本thread可访问

//
在编程中,可以在变量名前面加上这些前缀以区分。

?

数据类型

1

2

3

4

5

6

7

8

9

//
内建矢量类型:

int1,int2,int3,int4,float1,float2,
float3,float4 ...

//
纹理类型:

texture<Type,
Dim, ReadMode>texRef;

//
内建dim3类型:定义grid和block的组织方法。例如:

dim3
dimGrid(2, 2);

dim3
dimBlock(4, 2, 2);

//
CUDA函数CPU端调用方法

kernelFoo<<<dimGrid,
dimBlock>>>(argument);

?

函数定义

1

2

3

4

5

6

7

8

9

10

__device__
//
执行于Device,仅能从Device调用。限制,不能用&取地址;不支持递归;不支持static variable;不支持可变长度参数

__global__
//
void: 执行于Device,仅能从Host调用。此类函数必须返回void

__host__
//
执行于Host,仅能从Host调用,是函数的默认类型

//
在执行kernel函数时,必须提供execution configuration,即<<<....>>>的部分。

//  
例如:

__global__
void

KernelFunc(...);

dim3
DimGrid(100, 50);
//
5000 thread blocks

dim3
DimBlock(4, 8, 8);
//
256 threads per block

size_t

SharedMemBytes = 64;
//
64 bytes of shared memory

KernelFunc<<<
DimGrid, DimBlock, SharedMemBytes >>>(...);

?

数学函数

1

2

CUDA包含一些数学函数,如sinpow等。每一个函数包含有两个版本,

例如正弦函数sin,一个普通版本sin,另一个不精确但速度极快的__sin版本。

?

内置变量

1

2

3

4

5

/*

gridDim,
blockIdx, blockDim,

threadIdx,
wrapsize.

这些内置变量不允许赋值的

*/

?

编写程序

1

2

3

4

5

6

7

/*

目前CUDA仅能良好的支持C,在编写含有CUDA代码的程序时,

首先要导入头文件cuda_runtime_api.h。文件名后缀为.cu,使用nvcc编译器编译。

目前最新的CUDA版本为5.0,可以在官方网站下载最新的工具包,网址为:

https://developer.nvidia.com/cuda-downloads

该工具包内包含了ToolKit、样例等,安装起来比原先的版本也方便了很多。

*/

?

相关扩展

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

1
GPU硬件

//
i GPU一个最小单元称为Streaming Processor(SP),全流水线单事件无序微处理器,

包含两个ALU和一个FPU,多组寄存器文件(register

file,很多寄存器的组合),

这个SP没有cache。事实上,现代GPU就是一组SP的array,即SPA。

每一个SP执行一个thread

//
ii 多个SP组成Streaming Multiprocessor(SM)。

每一个SM执行一个block。每个SM包含8个SP;

2个special
function unit(SFU):

这里面有4个FPU可以进行超越函数和插值计算

MultiThreading
Issue Unit:分发线程指令

具有指令和常量缓存。

包含shared
memory

//
iii Texture Processor Cluster(TPC) :包含某些其他单元的一组SM

2
Single-Program Multiple-Data (SPMD)模型 

//
i CPU以顺序结构执行代码,

GPU以threads
blocks组织并发执行的代码,即无数个threads同时执行

//
ii 回顾一下CUDA的概念:

一个kernel程序执行在一个grid
of threads blocks之中

一个threads
block是一批相互合作的threads:

可以用过__syncthreads同步;

通过shared
memory共享变量,不同block的不能同步。

//
iii Threads block声明:

可以包含有1到512个并发线程,具有唯一的blockID,可以是1,2,3D

同一个block中的线程执行同一个程序,不同的操作数,可以同步,每个线程具有唯一的ID

3
线程硬件原理

//
i GPU通过Global block scheduler来调度block,

根据硬件架构分配block到某一个SM。

每个SM最多分配8个block,每个SM最多可接受768个thread

(可以是一个block包含512个thread

也可以是3个block每个包含256个thread(3*256=768!))。

同一个SM上面的block的尺寸必须相同。每个线程的调度与ID由该SM管理。

//
ii SM满负载工作效率最高!考虑某个Block,其尺寸可以为8*8,16*16,32*32

8*8:每个block有64个线程,

由于每个SM最多处理768个线程,因此需要768/64=12个block。

但是由于SM最多8个block,因此一个SM实际执行的线程为8*64=512个线程。

16*16:每个block有256个线程,SM可以同时接受三个block,3*256=768,满负载

32*32:每个block有1024个线程,SM无法处理!

//
iii Block是独立执行的,每个Block内的threads是可协同的。

//
iv 每个线程由SM中的一个SP执行。

当然,由于SM中仅有8个SP,768个线程是以warp为单位执行的,

每个warp包含32个线程,这是基于线程指令的流水线特性完成的。

Warp是SM基本调度单位,实际上,一个Warp是一个32路SIMD指令

。基本单位是half-warp。

如,SM满负载工作有768个线程,则共有768/32=24个warp

,每一瞬时,只有一组warp在SM中执行。

Warp全部线程是执行同一个指令,

每个指令需要4个clock

cycle,通过复杂的机制执行。

//
v 一个thread的一生:

Grid在GPU上启动;

block被分配到SM上;

SM把线程组织为warp;

SM调度执行warp;

执行结束后释放资源;

block继续被分配....

4
线程存储模型

//
i Register and local memory:线程私有,对程序员透明。

每个SM中有8192个register,分配给某些block,

block内部的thread只能使用分配的寄存器。

线程数多,每个线程使用的寄存器就少了。

//
ii shared memory:block内共享,动态分配。

如__shared__
float

region[N]。

shared
memory 存储器是被划分为16个小单元,

与half-warp长度相同,称为bank,每个bank可以提供自己的地址服务。

连续的32位word映射到连续的bank。

对同一bank的同时访问称为bank
conflict。

尽量减少这种情形。

//
iii Global memory:没有缓存!容易称为性能瓶颈,是优化的关键!

一个half-warp里面的16个线程对global
memory的访问可以被coalesce成整块内存的访问,如果:

数据长度为4,8或16bytes;地址连续;起始地址对齐;第N个线程访问第N个数据。

Coalesce可以大大提升性能。

//
uncoalesced

Coalesced方法:如果所有线程读取同一地址,

不妨使用constant
memory;

如果为不规则读取可以使用texture内存

如果使用了某种结构体,其大小不是4
8 16的倍数,

可以通过__align(X)强制对齐,X=4
8 16

时间: 2024-08-24 04:40:50

GPU & CPU编程的相关文章

五 浅谈CPU 并行编程和 GPU 并行编程的区别

前言 CPU 的并行编程技术,也是高性能计算中的热点,也是今后要努力学习的方向.那么它和 GPU 并行编程有何区别呢? 本文将做出详细的对比,分析各自的特点,为将来深入学习 CPU 并行编程技术打下铺垫. 区别一:缓存管理方式的不同 GPU:缓存对程序员不透明,程序员可根据实际情况操纵大部分缓存 (也有一部分缓存是由硬件自行管理). CPU:缓存对程序员透明.应用程序员无法通过编程手段操纵缓存. 区别二:指令模型的不同 GPU:采用 SIMT - 单指令多线程模型,一条指令配备一组硬件,对应32

CPU+GPU异构计算编程简介

分享一下我老师大神的人工智能教程吧.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net 异构计算(CPU + GPU)编程简介 1. 概念 所谓异构计算,是指CPU+ GPU或者CPU+ 其它设备(如FPGA等)协同计算.一般我们的程序,是在CPU上计算.但是,当大量的数据需要计算时,CPU显得力不从心.那么,是否可以找寻其它的方法来解决计算速度呢?那就是异构计算.例如可利用CPU(Central Processing

第三篇:GPU 并行编程的运算架构

前言 GPU 是如何实现并行的?它实现的方式较之 CPU 的多线程又有什么分别? 本文将做一个较为细致的分析. GPU 并行计算架构 GPU 并行编程的核心在于线程,一个线程就是程序中的一个单一指令流,一个个线程组合在一起就构成了并行计算网格,成为了并行的程序,下图展示了多核 CPU 与 GPU 的计算网格: 二者的区别将在后面探讨. 下图展示了一个更为细致的 GPU 并行计算架构: 该图表示,计算网格由多个流处理器构成,每个流处理器又包含 n 多块. 下面进一步对 GPU 计算网格中的一些概念

四 GPU 并行编程的存储系统架构

前言 在用 CUDA 对 GPU 进行并行编程的过程中,除了需要对线程架构要有深刻的认识外,也需要对存储系统架构有深入的了解. 这两个部分是 GPU 编程中最为基础,也是最为重要的部分,需要花时间去理解吸收,加深内功. 了解 GPU 存储系统架构的意义 CUDA 编程架构的设计思路本身也就是让程序员去使用缓存,而不是让缓存像 CPU 编程结构那样对程序员透明. 通过对所使用存储结构的优化,能够让程序的并行后的效果得到很大提高. 因此,这个问题是需要我们在开发全程中考虑的. 第一层:寄存器 每个流

《GPU高性能编程CUDA实战》中代码整理

CUDA架构专门为GPU计算设计了一种全新的模块,目的是减轻早期GPU计算中存在的一些限制,而正是这些限制使得之前的GPU在通用计算中没有得到广泛的应用. 使用CUDA C来编写代码的前提条件包括:(1).支持CUDA的图形处理器,即由NVIDIA推出的GPU显卡,要求显存超过256MB:(2).NVIDIA设备驱动程序,用于实现应用程序与支持CUDA的硬件之间的通信,确保安装最新的驱动程序,注意选择与开发环境相符的图形卡和操作系统:(3).CUDA开发工具箱即CUDA Toolkit,此工具箱

三 GPU 并行编程的运算架构

前言 GPU 是如何实现并行的?它实现的方式较之 CPU 的多线程又有什么分别?本文将做一个较为细致的分析. GPU 并行计算架构 GPU 并行编程的核心在于线程,一个线程就是程序中的一个单一指令流,一个个线程组合在一起就构成了并行计算网格,成为了并行的程序,下图展示了多核 CPU 与 GPU 的计算网格: 二者的区别将在后面探讨. 下图展示了一个更为细致的 GPU 并行计算架构: 该图表示,计算网格由多个流处理器构成,每个流处理器又包含 n 多块. 下面对 GPU 计算网格中的一些概念做细致分

CUDA __shared__ thread、block、grid之间的一维关系 (例子chapter5 dot点积(GPU高性能编程))

chapter5里重要的例子是dot,来解释一个block内多个thread的共享内存和同步. __shared__共享内存:“对于在GPU上启动的每个线程块,cuda c编译器都将创建该变量的一个副本.线程块中的每个线程都共享这块内存,并和其他线程块无关,这使一个线程块中多个线程能够在计算上进行通信和协作” __syncthreads():确保线程块中的每个线程都执行完__syncthreads()前面的语句后,在往下执行. 例子是Grid->一维Block->一维Thread: 通过实例代

TensorFlow指定GPU/CPU进行训练和输出devices信息

TensorFlow指定GPU/CPU进行训练和输出devices信息 1.在tensorflow代码中指定GPU/CPU进行训练 with tf.device('/gpu:0'): .... with tf.device('/gpu:1'): ... with tf.device('/cpu:0'): ... 2.输出devices的信息 在指定devices的时候往往不知道具体的设备信息,这时可用下面的代码查看对应的信息 进入Python环境 from tensorflow.python.c

GPU CPU在绘图方面的差异

关于绘图和动画有两种处理的方式:CPU(中央处理器)和GPU(图形处理器).在现代iOS设备中,都有可以运行不同软件的可编程芯片,但是由于历史原因,我们可以说CPU所做的工作都在软件层面,而GPU在硬件层面.总的来说,我们可以用软件(使用CPU)做任何事情,但是对于图像处理,通常用硬件会更快,因为GPU使用图像对高度并行浮点运算做了优化.由于某些原因,我们想尽可能把屏幕渲染的工作交给硬件去处理.问题在于GPU并没有无限制处理性能,而且一旦资源用完的话,性能就会开始下降了(即使CPU并没有完全占用