CUDA C 学习笔记_1.0.8
原子操作
CUDA的原子操作可以理解为:对变量的 “读取-修改-写入” 这三个操作进行捆绑或锁定,成为最小执行单位,不允许分解和打断。例如:当一个线程对某一个变量执行 “读取-修改-写入” 的操作时,此时第二个线程也需要对同一个变量执行 “读取-修改-写入” 的操作:
(1) 在不进行原子操作时,如果第二个线程读取时第一个线程还未写入,则读取和第一个线程相同的值,如果第一个线程已经写入完成,就会读取第一个线程写入的值,导致计算出现错误。
(2) 添加原子操作之后,如果在第一个线程操作完成之前,第二个线程的操作会被阻止,不允许其 “读取-修改-写入” ,只有等带没有线程操作该变量时才被允许。所以第二个线程只会读取到第一个线程写入的值。
基于这个机制,原子操作实现了对在多个线程间共享的变量的互斥保护,确保任何一次对变量的操作的结果的正确性。CUDA 中的原子操作与共享内存具有一些相似之处,都会使原本互不影响的各个线程之间产生依赖和相互作用。所不同的是,共享内存是 “ 线程块 ” 级别的,对于处于不同线程块之中的两个线程,依然不会因为共享内存的存在而产生相互作用;而原子操作是全局的,针对的是核函数启动时所唤醒的全部线程。
另外,从某种意义上讲,共享内存的使用初衷是为了提高计算效率,实现多个线程对重复数据的快速访问;原子操作更多的是为了保证计算结果正确的同时,避免 host (主机端) 的介入,在一定程度上会降低计算效率。
原子函数的使用
原子操作通过原子函数实现,常用的原子函数主要有以下几种:
(1) 加法
atomicAdd(&value, num); -----------value = value + num
(2) 减法
atomicSub(&value, num); -----------value = value - num
(3) 赋值
atomicExch(&value, num); ----------value = num
(4) 最大值
atomicMax(&value, num); -----------value = max(value, num)
(5) 最小值
atomicMin(&value, num); ------------value = max(value, num)
原子函数虽然目前功能比较有限,但其不仅可以使用在 ”线程块“ 级别,还可以使用在整个设备端的全局内存范围,并且最新的 CUDA 计算还可以实现整个计算域 (CPU+GPU) 级别的原子操作。