在GPU并行编程中,一般情况下,各个处理器都需要了解其他处理器的执行状态,在各个并行副本之间进行通信和协作,这涉及到不同线程间的通信机制和并行执行线程的同步机制。

共享内存“__share__”


CUDA中的线程协作主要是通过共享内存实现的。使用关键字“__share__”声明共享变量,将使这个变量驻留在共享内存中,该变量具有以下特征:

  • 位于线程块的共享存储器空间中
  • 与线程块具有相同的生命周期
  • 仅可通过块内的所有线程访问

对于GPU上启动的每个线程块,CUDA C编译器都将创建该变量的一个副本。 线程块中的每个线程都共享这块内存,但线程却无法看到也不能修改其他线程块的变量副本。 这就使得一个线程块中的多个线程能够在计算上进行通信和协作。而且,共享内存缓冲区驻留在物理GPU上,在访问共享内存时的延迟要远远低于访问普通缓冲区的延迟,使得共享内存的访问非常高效。

线程同步机制“__syncthreads()”


关键字“__share__”只是声明了共享变量,位于同一个线程块中的不同线程都可以访问该变量,如果没有同步机制,将会发生竞态条件 (Race Condition),导致错误的运行结果。
CUDA确保同步的方法是调用“__syncthreads()”。__syncthreads()将确保线程块中的每个线程都执行完 __syncthreads()前面的语句后,才会执行下一条语句。

以下是CUDA和OpenCV的应用中,绘制一幅图像,Grid的尺寸大小是60*60,Block的尺寸大小是10*10,在各个线程块内声明了一个共享变量sharedMem:

#include "cuda_runtime.h"
#include <highgui.hpp> using namespace cv; #define DIM 600 //图像长宽
#define PI 3.1415926535897932f __global__ void kernel(unsigned char *ptr)
{
// map from blockIdx to pixel position
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
int offset = x + y * blockDim.x * gridDim.x; __shared__ float sharedMem[16][16];
const float period = 128.0f;
sharedMem[threadIdx.x][threadIdx.y] =
255 * (sinf(x*2.0f*PI / period) + 1.0f) *
(sinf(y*2.0f*PI / period) + 1.0f) / 4.0f;
__syncthreads(); ptr[offset * 3 + 0] = 0;
ptr[offset * 3 + 1] = sharedMem[15 - threadIdx.x][15 - threadIdx.y];
ptr[offset * 3 + 2] = 0;
} // globals needed by the update routine
struct DataBlock
{
unsigned char *dev_bitmap;
}; int main(void)
{
DataBlock data;
cudaError_t error; Mat image = Mat(DIM, DIM, CV_8UC3, Scalar::all(0)); data.dev_bitmap = image.data;
unsigned char *dev_bitmap; error = cudaMalloc((void**)&dev_bitmap, 3 * image.cols*image.rows);
data.dev_bitmap = dev_bitmap; dim3 grid(DIM / 10, DIM / 10);
dim3 block(10, 10);
//DIM*DIM个线程块
kernel << <grid, block >> > (dev_bitmap); error = cudaMemcpy(image.data, dev_bitmap,
3 * image.cols*image.rows,
cudaMemcpyDeviceToHost); error = cudaFree(dev_bitmap); imshow("__share__ and __syncthreads()", image);
waitKey();
}


如果线程间不加入__syncthreads()同步机制,同一线程块内不同线程访问sharedMem,获取的结果可能是不一样的,生成的图像如下,有散乱的杂点:


加入__syncthreads()同步机制,保证了同一线程块中不同的线程都执行完成__syncthreads()这个集合点之前的部分之后,才继续往下执行,所以不同的线程访问sharedMem获取的结果是一致的,图像无杂散点,是一个规律的排布:


CUDA线程协作之共享存储器“__shared__”&&“__syncthreads()”的更多相关文章

  1. GPU编程自学5 —— 线程协作

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  2. 基于 Java 2 运行时安全模型的线程协作--转

    在 Java 2 之前的版本,运行时的安全模型使用非常严格受限的沙箱模型(Sandbox).读者应该熟悉,Java 不受信的 Applet 代码就是基于这个严格受限的沙箱模型来提供运行时的安全检查.沙 ...

  3. java线程系列之三(线程协作)

    本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7433673,转载请注明. 上一篇讲述了线程的互斥(同步),但是在很多情况 ...

  4. JAVA并发-线程协作

    这段时间有点忙,技术博客更新的比较少,今天更新一下相关并发的常用线程协作的类吧. ExecutorService 线程池,用于创造和复用线程,他有几种模式. 我举一个自定义线程池数量的例子如下 Exe ...

  5. Java多线程之线程协作

    Java多线程之线程协作 一.前言 上一节提到,如果有一个线程正在运行synchronized 方法,那么其他线程就无法再运行这个方法了.这就是简单的互斥处理. 假如我们现在想执行更加精确的控制,而不 ...

  6. 第41天学习打卡(死锁 Lock synchronized与Lock的对比 线程协作 使用线程池)

    死锁 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形.某一个同步块同时拥有"两个以上对象的锁"时 ...

  7. GPU(CUDA)学习日记(十一)------ 深入理解CUDA线程层次以及关于设置线程数的思考

    GPU线程以网格(grid)的方式组织,而每个网格中又包含若干个线程块,在G80/GT200系列中,每一个线程块最多可包含512个线程,Fermi架构中每个线程块支持高达1536个线程.同一线程块中的 ...

  8. CUDA线程

    建议先看看前言中关于存储器的介绍:点击打开链接 线程 首先介绍进程,进程是程序的一次执行,线程是进程内的一个相对独立的可执行的单元.若把进程称为任务的话,那么线程则是应用中的一个子任务的执行.举个简单 ...

  9. 【并行计算-CUDA开发】CUDA线程、线程块、线程束、流多处理器、流处理器、网格概念的深入理解

    GPU的硬件结构,也不是具体的硬件结构,就是与CUDA相关的几个概念:thread,block,grid,warp,sp,sm. sp: 最基本的处理单元,streaming processor  最 ...

随机推荐

  1. sprinng in action 第四版第六章中的ValidationMessages.properties不起作用

    文件名必须是ValidationMessages.properties,必须放在类的根目录下

  2. element ui源码解析 -- button篇

    要看源码就得从最简单的开始,button够简单的了,就从他开始吧. 安装依赖后源码目录在:node_modules/element-ui/packages中,可以看到这里的文件夹命名是不是很熟悉,就是 ...

  3. 使用u盘量产工具修复写保护的u盘

    自己的u盘突然提示写保护,而且也没有写保护开关,怎么都写不进文件,试了很多办法都无法去除写保护,最后找了一个u盘量产工具,搞定: 插上u盘后,会检测到u盘,点“开始"后静静等待它完成,u盘又 ...

  4. RDA安装

    解压到/home/oracle下面     $ cp /home/oracle/rda $ perl rda.pl -cv   运行上面的命令,如果最后一行出现下面所示,说明没问题       No ...

  5. [Recompose] Add Lifecycle Hooks to a Functional Stateless Component using Recompose

    Learn how to use the 'lifecycle' higher-order component to conveniently use hooks without using a cl ...

  6. 关于LayoutParams 分类: H1_ANDROID 2013-10-27 20:34 776人阅读 评论(0) 收藏

    每一个布局均有一个叫LayoutParams的内部类,如: LinearLayout.LayoutParams  RelativeLayout.LayoutParams  AbsoluteLayout ...

  7. 定位导致物化视图无法快速刷新的原因 分类: H2_ORACLE 2013-08-08 23:04 335人阅读 评论(0) 收藏

    转载自:http://yangtingkun.itpub.net/post/468/13318 物化视图的快速刷新采用了增量的机制,在刷新时,只针对基表上发生变化的数据进行刷新.因此快速刷新是物化视图 ...

  8. jquery ajax跨域 thinkphp getjson

    jquery中post的应该是不能跨域,网上说get的可以跨域,但是我试了一下也不行,然后就进行最后的拼搏getjson,结果成功,哈哈 js处写作 $.getJSON( "/index.p ...

  9. 用Ajax图片上传、预览、修改图片

    首选图片的上传和下载并不是很难,但要注意细节. 一,给出前端图片上传的html代码 1.图片上传的控件 <img src="/${res}/images/default.png&quo ...

  10. [RxJS] Use RxJS concatMap to map and concat high order observables

    Like switchMap and mergeMap, concatMap is a shortcut for map() followed by a concatAll(). In this le ...