前言

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

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

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

块内同步函数:__syncthreads ()

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

代码示例

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

  1. // 相关 CUDA 库
  2. #include "cuda_runtime.h"
  3. #include "cuda.h"
  4. #include "device_launch_parameters.h"
  5.  
  6. // 此头文件包含 __syncthreads ()函数
  7. #include "device_functions.h"
  8.  
  9. #include <iostream>
  10. #include <cstdlib>
  11.  
  12. using namespace std;
  13.  
  14. const int N = ;
  15.  
  16. // 块数
  17. const int BLOCK_data = ;
  18. // 各块中的线程数
  19. const int THREAD_data = ;
  20.  
  21. // CUDA初始化函数
  22. bool InitCUDA()
  23. {
  24. int deviceCount;
  25.  
  26. // 获取显示设备数
  27. cudaGetDeviceCount (&deviceCount);
  28.  
  29. if (deviceCount == )
  30. {
  31. cout << "找不到设备" << endl;
  32. return EXIT_FAILURE;
  33. }
  34.  
  35. int i;
  36. for (i=; i<deviceCount; i++)
  37. {
  38. cudaDeviceProp prop;
  39. if (cudaGetDeviceProperties(&prop,i)==cudaSuccess) // 获取设备属性
  40. {
  41. if (prop.major>=) //cuda计算能力
  42. {
  43. break;
  44. }
  45. }
  46. }
  47.  
  48. if (i==deviceCount)
  49. {
  50. cout << "找不到支持 CUDA 计算的设备" << endl;
  51. return EXIT_FAILURE;
  52. }
  53.  
  54. cudaSetDevice(i); // 选定使用的显示设备
  55.  
  56. return EXIT_SUCCESS;
  57. }
  58.  
  59. // 此函数在主机端调用,设备端执行。
  60. __global__
  61. static void Sum (int *data,int *result)
  62. {
  63. // 声明共享内存 (数组)
  64. extern __shared__ int shared[];
  65. // 取得线程号
  66. const int tid = threadIdx.x;
  67. // 获得块号
  68. const int bid = blockIdx.x;
  69.  
  70. shared[tid] = ;
  71. // 有点像网格计算的思路
  72. for (int i=bid*THREAD_data+tid; i<N; i+=BLOCK_data*THREAD_data)
  73. {
  74. shared[tid] += data[i];
  75. }
  76.  
  77. // 块内线程同步函数
  78. __syncthreads ();
  79.  
  80. // 每个块内索引为 0 的线程对其组内所有线程的求和结果再次求和
  81. if (tid == ) {
  82. for(int i = ; i < THREAD_data; i++) {
  83. shared[] += shared[i];
  84. }
  85. // result 数组存放各个块的计算结果
  86. result[bid] = shared[];
  87. }
  88. }
  89.  
  90. int main ()
  91. {
  92. // 初始化 CUDA 编译环境
  93. if (InitCUDA()) {
  94. return EXIT_FAILURE;
  95. }
  96. cout << "成功建立 CUDA 计算环境" << endl << endl;
  97.  
  98. // 建立,初始化,打印测试数组
  99. int *data = new int [N];
  100. cout << "测试矩阵: " << endl;
  101. for (int i=; i<N; i++)
  102. {
  103. data[i] = rand()%;
  104. cout << data[i] << " ";
  105. if ((i+)% == ) cout << endl;
  106. }
  107. cout << endl;
  108.  
  109. int *gpudata, *result;
  110.  
  111. // 在显存中为计算对象开辟空间
  112. cudaMalloc ((void**)&gpudata, sizeof(int)*N);
  113. // 在显存中为结果对象开辟空间
  114. cudaMalloc ((void**)&result, sizeof(int)*BLOCK_data);
  115.  
  116. // 将数组数据传输进显存
  117. cudaMemcpy (gpudata, data, sizeof(int)*N, cudaMemcpyHostToDevice);
  118. // 调用 kernel 函数 - 此函数可以根据显存地址以及自身的块号,线程号处理数据。
  119. Sum<<<BLOCK_data,THREAD_data,THREAD_data*sizeof (int)>>> (gpudata,result);
  120.  
  121. // 在内存中为计算对象开辟空间
  122. int *sumArray = new int[BLOCK_data];
  123. // 从显存获取处理的结果
  124. cudaMemcpy (sumArray, result, sizeof(int)*BLOCK_data, cudaMemcpyDeviceToHost);
  125.  
  126. // 释放显存
  127. cudaFree (gpudata);
  128. cudaFree (result);
  129.  
  130. // 计算 GPU 每个块计算出来和的总和
  131. int final_sum=;
  132. for (int i=; i<BLOCK_data; i++)
  133. {
  134. final_sum += sumArray[i];
  135. }
  136.  
  137. cout << "GPU 求和结果为: " << final_sum << endl;
  138.  
  139. // 使用 CPU 对矩阵进行求和并将结果对照
  140. final_sum = ;
  141. for (int i=; i<N; i++)
  142. {
  143. final_sum += data[i];
  144. }
  145. cout << "CPU 求和结果为: " << final_sum << endl;
  146.  
  147. getchar();
  148.  
  149. return ;
  150. }

运行结果

  

  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. 总结js(Iframe、window.open、window.showModalDialog)父窗口与子窗口之间的操作

    http://hi.baidu.com/yashua839/blog/item/131fdb2fe547ef221f3089af.html一.Iframe 篇 //&&&&am ...

  2. TensorFlow 安装 Ubuntu14.04

    1.Install pip (or pip3 for python3) if it is not already installed: # Ubuntu/Linux 64-bit $ sudo apt ...

  3. sim800L调试问题

    SIM800L默认上电开机,若此时没有把rst和pwk引脚提前设置好,SIM800l会使stm32进入硬件中断(这可能是因为方面电源的原因导致的),同时sim800L开机后需要一定的时间稳定下来,建议 ...

  4. SQLyog之MySQL客户端的下载、安装和使用(旗舰版)(推荐)

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  5. vue使用vue-resource,进行网络请求

    首先使用vue-resource,需要安装 : https://blog.csdn.net/qq_36947128/article/details/72832977 下面我写的一个例子: 网络请求应该 ...

  6. Redis(十四):主从复制

    当数据量变得庞大的时候,读写分离还是很有必要的.同时避免一个redis服务宕机,导致应用宕机的情况,我们启用sentinel(哨兵)服务,实现主从切换的功能. 主从复制 Redis 支持简单且易用的主 ...

  7. 【转】logstash配置java环境

    1.bin/logstash,新增 JAVA_CMD=/home/admin/soft/jdk1.8.0_121/bin JAVA_HOME=/home/admin/soft/jdk1.8.0_121 ...

  8. FreeRTOS 系统时钟节拍和时间管理

    以下转载自安富莱电子: http://forum.armfly.com/forum.php FreeRTOS 的时钟节拍任何操作系统都需要提供一个时钟节拍,以供系统处理诸如延时. 超时等与时间相关的事 ...

  9. mysql学习笔记1---mysql ERROR 1045 (28000): 错误解决办法(续:深入分析)

    在命令行输入mysql -u root –p,输入密码,或通过工具连接数据库时,经常出现下面的错误信息,详细该错误信息很多人在使用MySQL时都遇到过. ERROR 1045 (28000): Acc ...

  10. 【Spring实战】—— 3 使用facotry-method创建单例Bean总结

    如果有这样的需求: 1 不想再bean.xml加载的时候实例化bean,而是想把加载bean.xml与实例化对象分离. 2 实现单例的bean 以上的情况,都可以通过工厂方法factory-metho ...