前言

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

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

  至于块之间的同步,需要使用到 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. 微信小程序中同步 异步的使用

    https://www.jianshu.com/p/e92c7495da76   微信小程序中使用Promise进行异步流程处理 https://www.cnblogs.com/cckui/p/102 ...

  3. 微信小程序中使用Async-await方法异步请求变为同步请求

    微信小程序中有些 Api 是异步的,无法直接进行同步处理.例如:wx.request.wx.showToast.wx.showLoading等.如果需要同步处理,可以使用如下方法: 注意: Async ...

  4. WeChat-SmallProgram:微信小程序中使用Async-await方法异步请求变为同步请求

    微信小程序中有些 Api 是异步的,无法直接进行同步处理.例如:wx.request.wx.showToast.wx.showLoading 等.如果需要同步处理,可以使用如下方法: 提示:Async ...

  5. CUDA程序的调试总结【不定时更新】

    1 )CUDA的程序,经常犯,但是很难发现的一个错误就是同步问题. 描述下实例 for (k = 0; k < N; k+=BS) { sda[tx] = gda[tx+index]; __sy ...

  6. zz剖析为什么在多核多线程程序中要慎用volatile关键字?

    [摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...

  7. iOS中线程同步基本详解

    为什么使用线程同步技术:多个线程是同时执行的 如果多个线程同时操作一个资源 会造成此资源的数据错乱 线程同步简介 线程同步,多条线程按顺序地访问某个资源 注意:此处的同步不是一起执行的意思 是一个一个 ...

  8. JAVA中线程同步的方法(7种)汇总

    同步的方法: 一.同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法.在调用该方法前,需要获得内置锁,否则就 ...

  9. C#中的线程(中)-线程同步

    1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...

随机推荐

  1. Split的应用

    public string qu(string ss) { string s1 = "" ; string[] s = ss.Split(); for (int i = 0; i ...

  2. PostgreSQL数据库系统优点

    PostgreSQL 是世界上可以获得的最先进的开放源码的数据库系统, 它提供了多版本并行控制,支持几乎所有 SQL 构件(包括子查询,事务和用户定 义类型和函数), 并且可以获得非常广阔范围的(开发 ...

  3. 安装VMWare tools,以及解决安装后/mnt中有hgfs但没共享文件的方法

    一.首先是安装VMWare tools   安装过程可参考:Installing VMware Tools in an Ubuntu virtual machine   安装成功后,可看的如下信息: ...

  4. 理解ROS rqt_console和 roslaunch

    1.使用rqt_console和roslaunch 这篇教程将介绍使用rqt_console和rqt_logger_level来调试以及使用roslaunch一次启动许多nodes.如果你使用ROS  ...

  5. LICEcap

    LICEcap是一款简洁易用的动画屏幕录制软件,它可将屏幕录像的内容直接保存为高质量(每帧颜色数量可超过256)GIF动态图片格式.并且支持特别标记鼠标操作动态效果.

  6. struts中获取域

    在struts的Action中,有三种方法可以得到request.session.servletContext域. 1.通过ServletActionContext类获取对象 HttpServletR ...

  7. 获取UIColor中的RGB值(本人亲测多个获取RGB值的方法,这个最有效)

    在自己研发的项目个人项目中,碰到一个从颜色中获取RGB值的需求. 在网上找了许久,也有一些方法可以获取RGB值,但不能获取黑白以及灰色的值(他们是非RGB颜色空间,不清楚什么意思,反正亲测确实获取不了 ...

  8. JavaScript 之 走马灯

    1.原理分析:首先截取字符串的最后一位用Last表示,再截取剩余字符串用Rest表示,拼接字符串Last + Rest, 此事字符串是不会动的,还需要一个函数setInterval(javascrip ...

  9. Android布局---相对布局

    Android布局分为五大类:相对布局.线性布局.表格布局.帧布局.网格布局 相对布局 语法格式: <RelativeLayout xmlns:android="http://sche ...

  10. setLayoutParams getLayoutParams

    继承关系:java.lang.Object ↳ android.view.ViewGroup.LayoutParams ↳ android.view.ViewGroup.MarginLayoutPar ...