CUDA基础的基础教程:初探原子操作和CUDA流

1.原子操作

熟悉操作系统的读者对与原子操作不会陌生,原子操作代表不可被分割的操作,也就是最小的计算机可执行程序的单元。在CUDA中,也有这样的原子操作,我们使用原子操作之后,会锁定空间,防止其他的进程访问。由于CUDA的超多线程并行运算的特性,我们可以利用CUDA中的原子操作来优化我们的程序。

1.1 CUDA原子操作

CUDA可供使用的原子操作有很多,我们只介绍最基本的一个atomicAdd,这个函数接受两个同类型的参数,并将第二个值加到第一个值上。我们举一个简单的直方图统计的例子来说明在什么情况下使用原子操作。

1.2 直方图统计GPU版:

我们现在有一个很长的字符串,我们要统每个字符的出现次数。如果利用CPU进行计算,我们会在很长的时间来遍历一遍字符串。利用CUDA,我们可以将整个任分配给多个进程块,或者我们可以启动一个256个进程组成的进程块。总之,我们发现进程块的数量是CUDA单元的2倍时,效率是最高的。我们之后可以利用原子操作来执行对应的任务。
但是我们开率这样一个问题,当有很多个原子操作时,内存是有限的,当有很多个原子操作试图访问比较小的全局内存的时候,反而会因为信号竞争产生大量的堵塞,这个时候我们需要用上回讲到的共享内存了。
我们首先分配同样大小的共享内存,然后先讲这个进程处理的值暂存在共享内存。之后我们在把他们合并在一起。同样的,我们要在全部的初步统计结束之后,让所有的线程同步。当然,同步的过程同样使用原子操作。
使用原子操作的确会加快速度,有的时候增加更多的原子操作未必是件坏事。

2.CUDA流:

CUDA的效率十分惊人,但是我们之前介绍只是在同样的数据上进行着相同的任务,使用了CUDA流之后,我们就可以在GPU处理器上同时完成更多的任务了。

2.1 页锁定内存:

其实就是固定的内存,要知道这个不是GOU上的内存,而是主机分配的内存。在主机上的内存可能会被自动分页或者移动到了硬盘上,所以我们固定了内存,相当于固定了地址,GPU在知道地址的情况下,相当于可以直接访问(DMA),这样就不会被总线限制速度了

为了使用固定内存,我们需要新的函数cudaHostAlloc用法和cudaMalloc是一样的

2.2 什么是CUDA流:

CUDA流是什么呢?我想到输出输入流,但是这个和CUDA中的流是有区别的,CUDA中的流相当于是一个操作队列,当然这个操作队列可能就是一个进程上的任务,通过使用多个流,我们就可以更高效的完成计算任务。

使用流就不得不使用之前说过的固定内存,因为涉及到大量的内存交换,我们需要更高效的办法,由于固定内存上的交换是独立的,我们可以同时交换内存和执行核函数。

比如我们可以让两个流做同步,假设我们需要讲A+B的结果存到C中,我们就可以趁第一个流运行核函数的时候,第二个流将数据复制进GPU,以此类推
CUDA基础的基础教程:初探原子操作和CUDA流_第1张图片

偷了个图,大致的流程就是这个样子。
同时,复制内存就不能使用原来的方法了,CUDA中有cudaMemecpyAsync,这是一种异步的方法,也就是说我们不知道他什么之后会被执行,但是我们我们把他塞入流中,就知道这个代码在执行之后的操作就一定执行完毕了。好比我们不知道什么时候会执行复制或者执行核函数,先排一个这样的复制操作进去,等到另一个队列执行核函数的时候,复制引擎空下来就可以执行啦。
总之合理的使用流进行数据交换和运算是一个很好的方法,具体的代码请参考《CUDA By Example》中的第10章的代码。

总结:

我们为了尽可能的让任务并行执行,采用了流的方法、当然我们要注意要使用固定内存,并且异步的执行内存复制操作。同时我们要理解硬件执行顺序之后在进行设计。

你可能感兴趣的:(CUDA)