这节主要涉及到一个多线程情况下存在的数据竞争问题 -- 多个线程同时访问共享数据时,由于没有正确的同步机制,导致数据出现不一致的情况。

  • C/C++ 多线程中,可以通过互斥锁(mutex)、原子操作(atomic,C++11 也提供了原子操作库,如std::atomic,用于实现原子加法、原子赋值等操作)、线程局部变量(C++11提供了thread_local,它为每个线程分配了一份独立的内存,使得不同线程之间的局部变量互不干扰。这样可以避免多个线程访问同一共享数据时的数据竞争问题)以及使用同步代码块(using)、全局变量等措施来避免多线程操作

  • 由于CUDA采用单指令多线程(Single-Instruction Multiple-Thread,SIMT)来管理和执行 GPU 上的众多线程,因此在操作数据时,也会存在数据竞争等问题,而在CUDA中避免数据竞争的方法主要包括使用原子操作适当的索引计算

原子操作可以确保在多线程环境下对共享变量进行原子的读写操作,从而避免数据竞争。

利用线程索引计算可以使得每个线程操作不同的数据元素,避免竞争

上代码:

__global__ void increment_naive(int *g)
{
int idx = threadIdx.x + blockIdx.x * blockDim.x;
idx = idx % ARRAYSIZE;
// g[idx] = g[idx] + 1;
printf("before %d:%d\n", idx, g[idx]);
atomicAdd(& g[idx], 1);
printf("after %d:%d\n", idx, g[idx]);
}

从主机代码中将数据拷贝到设备端,默认都是放在设备的全局内存中(也就是global memory中),在运行核函数时从全局内存中读数据、运算、写入全局内存

  • 首先多个线程会同时从全局内存中取出g[idx]的数据,
  • 运算, g[idx] = g[idx] + 1; 虽然此处计算了,也写入了对应的g[idx]中,但是其他线程并没有拿到这个线程计算的的结果,他们用的数据还是最开始的原始数据,导致最后计算出错

    atomicAdd(& g[idx], 1);该函数为CUDA提供的原子函数接口,该接口详细描述如下:
int atomicAdd(int* address, int val);
unsigned int atomicAdd(unsigned int* address,
unsigned int val);
unsigned long long int atomicAdd(unsigned long long int* address,
unsigned long long int val);
float atomicAdd(float* address, float val);
double atomicAdd(double* address, double val);
__half2 atomicAdd(__half2 *address, __half2 val);
__half atomicAdd(__half *address, __half val);
__nv_bfloat162 atomicAdd(__nv_bfloat162 *address, __nv_bfloat162 val);
__nv_bfloat16 atomicAdd(__nv_bfloat16 *address, __nv_bfloat16 val);

上面是 device-wide atomicAdd 不同数据类型的定义,表示从第一个参数 address 指针指向的内存地址(可以是全局内存或共享内存)中读取16位、32位或64位数据(记做旧值 old),与第二个参数val 做一个加法计算(old + val),然后将求和结果写回到 address 指针指向的内存地址中,并返回未做计算前的旧值 old。这三个操作在一个原子事务中执行。

  • 32位 floating-point 浮点版本的 atomicAdd() 仅由计算能力 2.x 及更高版本的设备支持。
  • 64位 floating-point 浮点版本的 atomicAdd()只被具有计算能力 6.x 及更高版本的设备支持。
  • 32位 __half2 浮点版本的 atomicAdd() 只被具有计算能力 6.x 及更高版本的设备支持。对于两个__half 或__nv_bfloat16 元素,分别保证 __half2 或 __nv_bfloat162 添加操作的原子性;整个__half2 或 __nv_bfloat162 不能保证作为单个32位访问是原子的。
  • 16位 __half 浮点版本的 atomicAdd() 仅由计算能力为 7.x 及更高版本的设备支持。
  • 16位 __nv_bfloat16 浮点版本的 atomicAdd() 仅由计算能力为 8.x 及更高版本的设备支持。

CUDA原子操作的更多相关文章

  1. 5.1 CUDA atomic原子操作

    和许多多线程并行问题一样,CUDA也存在互斥访问的问题,即当一个线程改变变量X,而另外一个线程在读取变量X的值,执行原子操作类似于有一个自旋锁,只有等X的变量在改变完成之后,才能执行读操作,这样可以保 ...

  2. 【CUDA并行程序设计系列(1)】GPU技术简介

    http://www.cnblogs.com/5long/p/cuda-parallel-programming-1.html 本系列目录: [CUDA并行程序设计系列(1)]GPU技术简介 [CUD ...

  3. 《GPU高性能编程CUDA实战》附录一 高级原子操作

    ▶ 本章介绍了手动实现原子操作.重构了第五章向量点积的过程.核心是通过定义结构Lock及其运算,实现锁定,读写,解锁的过程. ● 章节代码 #include <stdio.h> #incl ...

  4. CUDA atomic原子操作

    CUDA的原子操作可以理解为对一个变量进行"读取-修改-写入"这三个操作的一个最小单位的执行过程,这个执行过程不能够再分解为更小的部分,在它执行过程中,不允许其他并行线程对该变量进 ...

  5. CUDA: 原子操作

    1.1以上计算功能集支持全局内存上的原子操作, 1.2以上支持共享内存上的原子操作. atomicAdd(add,y)将生成一个原子的操作序列,这个操作序列包括读取地址addr处的值,将y增加到这个值 ...

  6. CUDA 进阶学习

    CUDA基本概念 CUDA网格限制 1.2CPU和GPU的设计区别 2.1CUDA-Thread 2.2CUDA-Memory(存储)和bank-conflict 2.3CUDA矩阵乘法 3.1 全局 ...

  7. CUDA从入门到精通

    http://blog.csdn.net/augusdi/article/details/12833235 CUDA从入门到精通(零):写在前面 在老板的要求下.本博主从2012年上高性能计算课程開始 ...

  8. 5.2 CUDA Histogram直方图

    什么是Histogramming Histogramming是一种从大的数据集中提取典型特征和模式的方式. 在统计学中,直方图(英语:Histogram)是一种对数据分布情况的图形表示,是一种二维统计 ...

  9. CUDA C Best Practices Guide 在线教程学习笔记 Part 1

    0. APOD过程 ● 评估.分析代码运行时间的组成,对瓶颈进行并行化设计.了解需求和约束条件,确定应用程序的加速性能改善的上限. ● 并行化.根据原来的代码,采用一些手段进行并行化,例如使用现有库, ...

  10. CUDA C

    一.CUDA结构 硬件:GPU(Graphics Processing Unit)   SM(Streaming Multiprocessor)     SP(Streaming Processor) ...

随机推荐

  1. Docker - 部署IT运维管理平台CAT

    原文链接:https://mp.weixin.qq.com/s/Ld9OLnmHP1IAc0Ofo-RzeQ 一.CAT介绍(略) 二.环境规划(略) 三.检查环境(略) 四.部署cat镜像 1.下载 ...

  2. WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战

    开源项目名称:leagueoflegends-OpenSilver 作者:Vicky&James leagueoflegends-opensilver:https://github.com/j ...

  3. oauth2.0 判断接口是否允许跨域

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 【数值计算方法】数值积分&微分-python实现

    目录 数值积分 1. 引言 2. 几个常用积分公式及其复合公式 2.1 求积公式 2.2 代数精度 2.3 复合积分 2.4 常用积分公式的python实现 3. 变步长方法与外推加速技术 4. 牛顿 ...

  5. vim使用技巧记录

    1.查找 '/' + 要找的字符串(正则表达式) + Enter # 查找偏移 'n': 查找下一个 'N': 查找上一个 大小写敏感性:字符串尾接\c不敏感,\C敏感 可以~/.vimrc在配置中配 ...

  6. 本地如何访问vue2 生成的dist代码

    前言 当你使用 Vue CLI 或其他构建工具构建 Vue 2 项目时,它会生成一个 dist 文件夹,这个文件夹包含了你项目的生产环境版本的静态资源文件(HTML.JavaScript 和 CSS) ...

  7. Go是怎么解决包依赖管理问题的?

    我们先来了解一下 Go 构建模式的演化过程,弄清楚 Go 核心开发团队为什么要引入 Go module 构建模式. Go 构建模式时怎么演化的? Go 程序由 Go 包组合而成的,Go 程序的构建过程 ...

  8. go 结构体多字段多因素排序

    前言 有时候我们需要处理一份数据,需要多个字段作为条件,联合进行排序. 代码 package main import ( "fmt" "sort" ) // 数 ...

  9. Xshell连接有跳板机(堡垒机)的服务器

    一.Xshell直连有跳板机的服务器 跳板机IP:112.74.163.161 端口号: 22     服务器IP:47.244.217.66 端口号:22 1. 新建跳板机会话 点击连接,主机和端口 ...

  10. vue学习二(计算属性computed和监听器watch)

    1.1.computed  计算属性 先写注意事项把:computed和methods的区别 //computed定义的方法我们是以属性访问的形式调用的{{computedTest}}    comp ...