第五篇:CUDA 并行程序中的同步
前言
在并发,多线程环境下,同步是一个很重要的环节。同步即是指进程/线程之间的执行顺序约定。
本文将介绍如何通过共享内存机制实现块内多线程之间的同步。
至于块之间的同步,需要使用到 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 并行程序中的同步的更多相关文章
- CUDA 程序中的同步
前言 在并发,多线程环境下,同步是一个很重要的环节.同步即是指进程/线程之间的执行顺序约定. 本文将介绍如何通过共享内存机制实现块内多线程之间的同步. 至于块之间的同步,需要使用到 global me ...
- Windows核心编程 第十五章 在应用程序中使用虚拟内存
第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...
- 第五篇(那些JAVA程序BUG中的常见单词)
The left-hand side of an assignment must be a variable 赋值的左侧必须是变量 left-hand side 左边 assignment 赋值
- 第五篇:在SOUI中使用XML布局属性指引(pos, offset, pos2type)
窗口布局的概念 每一个UI都是由大量的界面元素构成的,在Windows编程,这些界面元素的最小单位通常称之为控件. 布局就是这些控件在主界面上的大小及相对位置. 传统的布局一般使用一个4个绝对坐标来定 ...
- 第二十五篇:在SOUI中做事件分发处理
不同的SOUI控件可以产生不同的事件.SOUI系统中提供了两种事件处理方式:事件订阅 + 事件处理映射表(参见第八篇:SOUI中控件事件的响应) 事件订阅由于直接将事件及事件处理函数连接,不存在事件分 ...
- 微信小程序开发系列五:微信小程序中如何响应用户输入事件
微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...
- spring-第五篇之spring容器中的bean
1.bean的基本定义和bean别名 2.容器中bean的作用域 singleton:单例模式,在整个spring IoC容器中,singleton作用域的bean将只生成一个实例. prototyp ...
- ASP.NET Core 学习笔记 第五篇 ASP.NET Core 中的选项
前言 还记得上一篇文章中所说的配置吗?本篇文章算是上一篇的延续吧.在 .NET Core 中读取配置文件大多数会为配置选项绑定一个POCO(Plain Old CLR Object)对象,并通过依赖注 ...
- 第十五篇:在SOUI中消息通讯
SOUI是一套基于Win32 SDK的窗口开发的一套DirectUI框架.在SOUI中除了有真窗口使用窗口消息通讯机制外,还有SOUI控件之间的通讯,及控件的事件处理等. 1.真窗口消息通讯 因此可以 ...
随机推荐
- Linux命令-文件搜索命令:grep
选项: -A 数字:表示查看满足条件行后的N条记录 -B 数字:表示查看满足条件行前的N条记录 -C 数字:表示查看满足条件行前后各N条记录 more /etc/inittab 分页查看inittab ...
- C#:列表视图操作类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- pandas set_index和reset_index的用法
1.set_index DataFrame可以通过set_index方法,可以设置单索引和复合索引. DataFrame.set_index(keys, drop=True, append=False ...
- java中GET方式提交和POST方式提交
java中GET方式提交的示例: /** * 获取关注列表; * @return */ @SuppressWarnings("unchecked") public static A ...
- Docker搭建本地私有仓库
安装部署一个私有的Docker Registry是引入.学习和使用Docker这门技术的必经之路之一.尤其是当Docker被所在组织接受,更多人.项目和产品开始接触和使用Docker时,存储和分发自制 ...
- J2EE学术交流感悟——分层
学术交流进行了一周,是关于J2EE的学术报告. 目的是让我们在学习的时候对"所学知识"有一个宏观的认识. 開始是以为环绕"J2EE"进行解说,怕自己没有 ...
- verilog之四位全加器的编译及仿真(用开源免费的软件——iverilog+GTKWave)
verilog之四位全加器的编译及仿真(用开源免费的软件——iverilog+GTKWave) 四位全加器的verilog的代码比比皆是,这里上一个比较简单的: /* 4位全加器全加器需要有输入输出, ...
- Java多线程简析——Synchronized(同步锁)、Lock以及线程池
Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...
- quick cocos2d-x 2.2.4 window环境调试
BabeLua简介 BabeLua是一款基于VS2012/2013(简称VS)的免费开源的Lua集成开发环境,在Lua编辑和调试方面,具有如下功能和特性: ●Lua语法高亮 ●语法检查 ●自动补全 ● ...
- 卡特兰数 catalan number
作者:阿凡卢 出处:http://www.cnblogs.com/luxiaoxun/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留 ...