前言

在并发,多线程环境下,同步是一个很重要的环节。同步即是指进程/线程之间的执行顺序约定。

本文将介绍如何通过共享内存机制实现块内多线程之间的同步。

至于块之间的同步,需要使用到 global memory,代价较为高昂,目前使用的情况也不多,就先不介绍了。

块内同步函数:__syncthreads ()

线程调用此函数后,该线程所属块中的所有线程均运行到这个调用点后才会继续往下运行。

代码示例

使用同步思想优化之前一篇博文中提到的数组求和程序。在新的程序中,让每个块中的第一个线程将块中所有线程的运算结果都加起来,然后再存入到结果数组中。这样,结果数组的长度与块数相等 (原来是和总线程数相等),大大降低了 CPU 端程序求和的工作量以及需要传递进/出显存的数据 (代码下方如果出现红色波浪线无视之):

 // 相关 CUDA 库
#include "cuda_runtime.h"
#include "cuda.h"
#include "device_launch_parameters.h" // 此头文件包含 __syncthreads ()函数
#include "device_functions.h" #include <iostream>
#include <cstdlib> using namespace std; const int N = ; // 块数
const int BLOCK_data = ;
// 各块中的线程数
const int THREAD_data = ; // CUDA初始化函数
bool InitCUDA()
{
int deviceCount; // 获取显示设备数
cudaGetDeviceCount (&deviceCount); if (deviceCount == )
{
cout << "找不到设备" << endl;
return EXIT_FAILURE;
} int i;
for (i=; i<deviceCount; i++)
{
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop,i)==cudaSuccess) // 获取设备属性
{
if (prop.major>=) //cuda计算能力
{
break;
}
}
} if (i==deviceCount)
{
cout << "找不到支持 CUDA 计算的设备" << endl;
return EXIT_FAILURE;
} cudaSetDevice(i); // 选定使用的显示设备 return EXIT_SUCCESS;
} // 此函数在主机端调用,设备端执行。
__global__
static void Sum (int *data,int *result)
{
// 声明共享内存 (数组)
extern __shared__ int shared[];
// 取得线程号
const int tid = threadIdx.x;
// 获得块号
const int bid = blockIdx.x; shared[tid] = ;
// 有点像网格计算的思路
for (int i=bid*THREAD_data+tid; i<N; i+=BLOCK_data*THREAD_data)
{
shared[tid] += data[i];
} // 块内线程同步函数
__syncthreads (); // 每个块内索引为 0 的线程对其组内所有线程的求和结果再次求和
if (tid == ) {
for(int i = ; i < THREAD_data; i++) {
shared[] += shared[i];
}
// result 数组存放各个块的计算结果
result[bid] = shared[];
}
} int main ()
{
// 初始化 CUDA 编译环境
if (InitCUDA()) {
return EXIT_FAILURE;
}
cout << "成功建立 CUDA 计算环境" << endl << endl; // 建立,初始化,打印测试数组
int *data = new int [N];
cout << "测试矩阵: " << endl;
for (int i=; i<N; i++)
{
data[i] = rand()%;
cout << data[i] << " ";
if ((i+)% == ) cout << endl;
}
cout << endl; int *gpudata, *result; // 在显存中为计算对象开辟空间
cudaMalloc ((void**)&gpudata, sizeof(int)*N);
// 在显存中为结果对象开辟空间
cudaMalloc ((void**)&result, sizeof(int)*BLOCK_data); // 将数组数据传输进显存
cudaMemcpy (gpudata, data, sizeof(int)*N, cudaMemcpyHostToDevice);
// 调用 kernel 函数 - 此函数可以根据显存地址以及自身的块号,线程号处理数据。
Sum<<<BLOCK_data,THREAD_data,THREAD_data*sizeof (int)>>> (gpudata,result); // 在内存中为计算对象开辟空间
int *sumArray = new int[BLOCK_data];
// 从显存获取处理的结果
cudaMemcpy (sumArray, result, sizeof(int)*BLOCK_data, cudaMemcpyDeviceToHost); // 释放显存
cudaFree (gpudata);
cudaFree (result); // 计算 GPU 每个块计算出来和的总和
int final_sum=;
for (int i=; i<BLOCK_data; i++)
{
final_sum += sumArray[i];
} cout << "GPU 求和结果为: " << final_sum << endl; // 使用 CPU 对矩阵进行求和并将结果对照
final_sum = ;
for (int i=; i<N; i++)
{
final_sum += data[i];
}
cout << "CPU 求和结果为: " << final_sum << endl; getchar(); return ;
}

运行结果

  

  PS:矩阵元素是随机生成的

小结

  共享内存,或者说这个共享数组是 CUDA 中实现同步最常用的方法。

第五篇:CUDA 并行程序中的同步的更多相关文章

  1. CUDA 程序中的同步

    前言 在并发,多线程环境下,同步是一个很重要的环节.同步即是指进程/线程之间的执行顺序约定. 本文将介绍如何通过共享内存机制实现块内多线程之间的同步. 至于块之间的同步,需要使用到 global me ...

  2. Windows核心编程 第十五章 在应用程序中使用虚拟内存

    第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...

  3. 第五篇(那些JAVA程序BUG中的常见单词)

    The left-hand side of an assignment must be a variable 赋值的左侧必须是变量 left-hand side 左边 assignment 赋值

  4. 第五篇:在SOUI中使用XML布局属性指引(pos, offset, pos2type)

    窗口布局的概念 每一个UI都是由大量的界面元素构成的,在Windows编程,这些界面元素的最小单位通常称之为控件. 布局就是这些控件在主界面上的大小及相对位置. 传统的布局一般使用一个4个绝对坐标来定 ...

  5. 第二十五篇:在SOUI中做事件分发处理

    不同的SOUI控件可以产生不同的事件.SOUI系统中提供了两种事件处理方式:事件订阅 + 事件处理映射表(参见第八篇:SOUI中控件事件的响应) 事件订阅由于直接将事件及事件处理函数连接,不存在事件分 ...

  6. 微信小程序开发系列五:微信小程序中如何响应用户输入事件

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  7. spring-第五篇之spring容器中的bean

    1.bean的基本定义和bean别名 2.容器中bean的作用域 singleton:单例模式,在整个spring IoC容器中,singleton作用域的bean将只生成一个实例. prototyp ...

  8. ASP.NET Core 学习笔记 第五篇 ASP.NET Core 中的选项

    前言 还记得上一篇文章中所说的配置吗?本篇文章算是上一篇的延续吧.在 .NET Core 中读取配置文件大多数会为配置选项绑定一个POCO(Plain Old CLR Object)对象,并通过依赖注 ...

  9. 第十五篇:在SOUI中消息通讯

    SOUI是一套基于Win32 SDK的窗口开发的一套DirectUI框架.在SOUI中除了有真窗口使用窗口消息通讯机制外,还有SOUI控件之间的通讯,及控件的事件处理等. 1.真窗口消息通讯 因此可以 ...

随机推荐

  1. Linux命令-目录处理命令:cd

    cd /tmp/shijiazhuang 切换到tmp目录下面的shijiazhuang目录 cd .. 切换到上一级目录

  2. Mysql Communications link failure 问题的解决

    问题现象 com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure The last p ...

  3. django abstract base class ---- 抽象基类

    抽象蕨类用于定义一些同享的列.类本身并不会在数据库端有表与之对应 一.例子: 1.定义一个叫Person 的抽象基类.Student 继承自Person from django.db import m ...

  4. 使用zlib模块实现HTTP服务端与客户端实现传输数据压缩

    现如今在处理http请求的时候,由于请求的资源较多,如果不启用压缩的话,那么页面请求的流量将会非常大.启用gzip压缩,在一定程度上会大大的提高页面性能. 因此这写一个使用Node.js实现在http ...

  5. unity, inspector listview

    inspector中实现列表框: public override void OnInspectorGUI(){ bool isDoubleClick=false;        Event e = E ...

  6. THREADSPOOL

    STPStartInfo stp = new STPStartInfo();//线程详细配置参数 stp.CallToPostExecute = CallToPostExecute.Always;// ...

  7. 《Google软件测试之道》- Google软件测试介绍

    <Google软件测试之道>- Google软件测试介绍 2015-05-21 目录 1 质量与测试  2 角色  3 组织结构  4 爬.走.跑  5 测试类型  相关链接 与Micro ...

  8. Matlab三维绘图

    三维绘图 1 三维绘图指令 类 别 指 令 说 明 网状图 mesh, ezmesh 绘制立体网状图 meshc, ezmeshc 绘制带有等高线的网状图 meshz 绘制带有“围裙”的网状图 曲面图 ...

  9. arm + fpga 核心板

  10. SQL合并数据

    --CREATE TABLE TMaterial (PMaterial INT,FName NVARCHAR(32))--INSERT INTO TMaterial--SELECT 1,'A' UNI ...