1.主机和设备间异步执行

  为了易于使用主机和设备间的异步执行,一些函数是异步的:在设备完全完成任务前,控制已经返回给主机线程了。它们是: 内核发射; 设备间数据拷贝函数; 主机和设备内拷贝小于64KB的存储器块时; 存储器拷贝函数中带有Async后缀的; 设置设备存储器的函数调用。

  程序员可通过将CUDA_LAUNCH_BLOCKING环境变量设置为1来全局禁用所有运行在系统上的应用的异步内核发射。提供这个特性只是为了调试,永远不能作为使软件产品运行得可靠的方式。 当应用通过CUDA调试器或CUDA profiler(cuda-gdb, CUDA Visual Profiler, Parallel Nsight)运行时,所有的内核发射都是同步的。

  2.数据传输和内核执行重叠

  一些计算能力1.1或更高的设备可在内核执行时,在分页锁定存储器和设备存储器之间拷贝数据。应用可以通过检查asyncEngineCount 设备属性查询这种能力,如果其大于0,说明设备支持数据传输和内核执行重叠。这种能力目前只支持不涉及CUDA数组和使用cudaMallocPitch()分配的二维数组的存储器拷贝( 见前文,可阅读“相关阅读”中的文章)。

  3. 并发内核执行

  一些计算能力2.x的设备可并发执行多个内核。应用可以检查concurrentKernels属性以查询这种能力)(后续文章将介绍),如果等于1,说明支持。 设备最大可并发执行的内核数目是16。 来自不同CUDA上下文的内核不能并发执行。 使用了许多纹理或大量本地存储器的内核和其它内核并发执行的可能性比较小。

  4. 并发数据传输

  在计算能力2.x的设备上,从主机分页锁定存储器复制数据到设备存储器和从设备存储器复制数据到主机分页锁定存储器,这两个操作可并发执行。 应用可以通过检查asyncEngineCount 属性查询这种能力,如果等于2,说明支持。

  5. 流

  应用通过流管理并发。流是一系列顺序执行的命令(可能是不同的主机线程发射)。另外,流之间相对无序的或并发的执行它们的命令;这种行为是没有保证的,而且不能作为正确性的的保证(如内核间的通信没有定义)。

  ①创建和销毁

  可以通过创建流对象来定义流,且可指定它作为一系列内核发射和设备主机间存储器拷贝的流参数。下面的代码创建了两个流且在分页锁定存储器中分配了一个名为hostPtr的浮点数组。

cudaStream_t stream[2];
for (int i = 0; i < 2; ++i)
cudaStreamCreate(&stream[i]);
float* hostPtr;
cudaMallocHost((void**)&hostPtr, 2 * size);

  下面的代码定义的每个流是一个由一次主机到设备的传输,一次内核发射,一次设备到主机的传输组成的系列。

for (int i = 0; i < 2; ++i){ cudaMemcpyAsync(inputDevPtr + i * size, hostPtr + i * size, size, cudaMemcpyHostToDevice, stream[i]); MyKernel<<<100, 512, 0, stream[i]>>> (outputDevPtr + i * size, inputDevPtr + i * size, size); cudaMemcpyAsync(hostPtr + i * size, outputDevPtr + i * size, size, cudaMemcpyDeviceToHost, stream[i]); }

  每个流将它的hostPtr输入数组的部分拷贝到设备存储器数组inputdevPtr,调用MyKernel()内核处理inputDevPtr,然后将结果outputDevPtr传输回hostPtr同样的部分。后文描述了例子中的流如何依赖设备的计算能力重叠。必须注意为了使用重叠hostPtr必须指向分页锁定主机存储器。

  调用cudaStreamDestroy()来释放流。

for (int i = 0; i < 2; ++i)
 cudaStreamDestroy(stream[i]);

  cudaStreamDestroy()等待指定流中所有之前的任务完成,然后释放流并将控制权返回给主机线程。

  ② 默认流

  内核启动和没有使用流参数的主机设备间数据拷贝,或者等价地将流参数设为0,此时发射到默认流。因此顺序执行。

  ③显式同步

  有很多方法显式的在流之间同步。

  cudaDeviceSynchronize()直到前面所有流中的命令都执行完。

  cudaStreamSynchronize()以某个流为参数,强制运行时等待该流中的任务都完成。可用于同步主机和特定流,同时允许其它流继续执行。

  cudaStreamWaitEvent()以一个流和一个事件为参数(后文将介绍),使得在调用cudaStreamWaitEvent()后加入到指定流的所有命令暂缓执行直到事件完成。流可以是0,此时在调用cudaStreamWaitEvent()后加入到所有流的所有命令等待事件完成。

  cudaStreamQuery()用于查询流中的所有之前的命令是否已经完成。

  为了避免不必要的性能损失,这些函数最好用于计时或隔离失败的发射或存储器拷贝。

  ④隐式同步

  如果是下面中的任何一种情况,来自不同流的两个命令也不能并发:分页锁定主机存储器分配,设备存储器分配,设备存储器设置,设备之间存储器拷贝,默认流中调用的任何CUDA命令, F.4.1节描述的一级缓存/共享存储器之间配置切换。

  对于支持并发内核执行的设备,任何需要依赖检测以确定内核发射是否完成的操作:

  1)只有来自CUDA上下文中的任何流中的所有的前面的内核启动的线程块开始执行,才能够开始执行;

  2)会阻塞CUDA上下文中后面任何流中所有的内核发射直至被检测的内核发射完成。

  需要依赖检测的操作包括同一个流中的一些其它类似被检查的发射的命令和流中的任何cudaStreamQuery()调用。因此,应用应当遵守这些指导以提升潜在的内核并发执行:

  1)所有独立操作应当在依赖操作之前发出,

  2)任何类型同步尽量延后。

  ⑤重叠行为

  两个流的重叠执行数量依赖于发射到每个流的命令的顺序和设备是否支持数据传输和内核执行重叠、并发内核执行、并发数据传输。

  例如,在不支持并发数据传输的设备上,前面例程的两个流并没有重叠,因为发射到流1的从主机到设备的存储器拷贝在发射到流0的从设备到主机的存储器拷贝之后,因此只有发射到流0的设备到主机的存储器拷贝完成它才开始。如果代码重写成如下方式(同时假设设备支持数据传输和内核执行重叠)。

for (int i = 0; i < 2; ++i)
cudaMemcpyAsync(inputDevPtr + i * size, hostPtr + i * size, size, cudaMemcpyHostToDevice, stream[i]);
for (int i = 0; i < 2; ++i)
MyKernel<<<100, 512, 0, stream[i]>>> (outputDevPtr + i * size, inputDevPtr + i * size, size);
for (int i = 0; i < 2; ++i)
cudaMemcpyAsync(hostPtr + i * size, outputDevPtr + i * size, size, cudaMemcpyDeviceToHost, stream[i]);

  此时发射到流1的从主机到设备的存储器拷贝和发射到流0的内核执行重叠。

  在支持并发数据传输的设备上,前文例程的两个流重叠:发射到流1的从主机到设备的存储器拷贝和发射到流0的设备到主机的存储器拷贝,甚至和发射到流0的内核执行(假设设备支持数据传输和内核执行重叠)。但是内核执行不可能重叠,因为发射到流1的第二个内核执行在发射到流0的设备到主机的存储器拷贝之后,因此会被阻塞直到发射到流0的内核执行完成。如果代码被重写成上面的样子,内核执行就重叠了(假设设备支持并发内核执行),因为发射到流1的第二个内核执行在发射到流0的设备到主机的存储器拷贝之前。然而在这种情况下,发射到流0的设备到主机的存储器只和发射到流1的内核执行的最后一个线程块重叠,这只占总内核执行时间的一小部分。

  6.事件

  通过在应用的任意点上异步地记载事件和查询事件是否完成,运行时提供了精密地监测设备运行进度和精确计时。当事件记载点前面,事件指定的流中的所有任务或者指定流中的命令全部完成时,事件被记载。只有记载点之前所有的流中的任务/命令都已完成,0号流的事件才会记载。

  ①创建和销毁

  下面的代码创建了两个事件:

cudaEvent_t start, stop; cudaEventCreate(&start); cudaEventCreate(&stop);

  以下面的方式销毁它们:

cudaEventDestroy(start); cudaEventDestroy(stop);

  ②过去的时间

  节建立的事件可以用下面的方式给3.2.5.5.1节的代码计时:

cudaEventRecord(start, 0); for (int i = 0; i < 2; ++i){ cudaMemcpyAsync(inputDev + i * size, inputHost + i * size, size, cudaMemcpyHostToDevice, stream[i]); MyKernel<<<100, 512, 0, stream[i]>>> (outputDev + i * size, inputDev + i * size, size); cudaMemcpyAsync(outputHost + i * size, outputDev + i * size, size, cudaMemcpyDeviceToHost, stream[i]); cudaEventRecord(stop, 0); cudaEventSynchronize(stop); float elapsedTime; cudaEventElapsedTime(&elapsedTime, start, stop);

  7.同步调用

  直到设备真正完成任务,同步函数调用的控制权才会返回给主机线程。在主机线程执行任何其它CUDA调用前,通过调用cudaSetDeviceFlags()并传入指定标签(参见参考手册)可以指定主机线程的让步,阻塞,或自旋状态。

更多内容请点击:

CUDA专区:http://cuda.it168.com/

CUDA论坛:http://cudabbs.it168.com/

CUDA编程接口:异步并发执行的概念和API的更多相关文章

  1. 【CUDA开发】CUDA编程接口(一)------一十八般武器

    子曰:工欲善其事,必先利其器.我们要把显卡作为通用并行处理器来做并行算法处理,就得知道CUDA给我提供了什么样的接口,就得了解CUDA作为通用高性能计算平台上的一十八般武器.(如果你想自己开发驱动,自 ...

  2. CUDA编程

    目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. ...

  3. 多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture

    目录 1.Futrue 2.FutureTask 3.CompletionService 4.CompletableFuture 5.总结 ================正文分割线========= ...

  4. 异步并发利器:实际项目中使用CompletionService提升系统性能的一次实践

    场景 随着互联网应用的深入,很多传统行业也都需要接入到互联网.我们公司也是这样,保险核心需要和很多保险中介对接,比如阿里.京东等等.这些公司对于接口服务的性能有些比较高的要求,传统的核心无法满足要求, ...

  5. Java编程的逻辑 (77) - 异步任务执行服务

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  6. CUDA C++编程接口:编译

    CUDA C++编程接口:编译 一.概述 CUDA C++为熟悉C++编程语言的用户提供了一个简单的路径,以方便地编写程序以执行该设备. 它由一组最小的扩展到C++语言和运行库. 在编程模型中引入了核 ...

  7. CUDA C编程接口技术分析

    CUDA C编程接口技术分析 编程接口 CUDA C为熟悉C编程语言的用户提供了一个简单的路径,可以方便地编写程序供设备执行. 它由C语言的最小扩展集和运行库组成. 核心语言扩展已经引入:cuda c ...

  8. C#并发编程之异步编程2

    C#并发编程之异步编程(二)   写在前面 前面一篇文章介绍了异步编程的基本内容,同时也简要说明了async和await的一些用法.本篇文章将对async和await这两个关键字进行深入探讨,研究其中 ...

  9. 【憩园】C#并发编程之异步编程(一)

    写在前面 C#5.0中,对异步编程进行了一次革命性的重构,引入了async和await这两个关键字,使得开发人员在不需要深刻了解异步编程的底层原理,就可以写出十分优美而又代码量极少的代码.如果使用得当 ...

随机推荐

  1. JVM内存管理中的垃圾回收策略

    JVM垃圾回收策略 1.静态内存分配和回收 编译时已经确定了内存空间大小,程序被加载后则一次性分配好内存空间.程序结束后,则对应栈帧撤销,分配的静态内存空间则被回收. 2.动态内存分配和回收 程序运行 ...

  2. js 图片加载失败处理方法

    在项目中不可避免会用到图片,尤其是列表,有时候图片会加载失败:这样就会显示一个很难看的坏图片缩略图:下面介绍两种方法,解决这个问题: 1.如果在你的项目中有引入jQuery插件,你可以使用error( ...

  3. 智课雅思词汇---二十一、名词性后缀acity是什么意思

    智课雅思词汇---二十一.名词性后缀acity是什么意思 一.总结 一句话总结:后缀:-acity [名词后缀] 构成抽象名词,表示性质.状态.情况.与形容词后缀-acious相对应 rapacity ...

  4. Jfinal整合百度富文本编辑器ueditor

    ueditor配置文件ueditor.config.js修改参数serverUrl:(改为要调用的action) 后台代码 package com.sandu.mega.admin.ueditor; ...

  5. mysql创建数据库表

    CREATE TABLE `product_info` ( `product_id` VARCHAR() NOT NULL, `product_name` VARCHAR() NOT NULL COM ...

  6. Spring Boot 简单日志配置

    在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件## 默认使用logback logging.level.root=error ...

  7. poj-1426-Find The Multiple(打表水过)

    思路: 2的最近可以整除的数是10 所以,很关键的一点,只要是偶数,例如: 6:2*3,可以拆分为能够被2整除和能够被3整除的乘积,所以,10*111=1110 144:72*2,8*9*2,2*2* ...

  8. CATransform3D 矩阵变换之立方体旋转实现细节 (转)

    原文地址 http://blog.csdn.net/ch_soft/article/details/7351896 第一部分.前几天做动画,使用到了CATransform3D ,由于没有学过计算机图形 ...

  9. javascript.history.go();

    转自:http://www.mikebai.com/Article/2009-11/757.html <input type=button value=刷新 onclick="wind ...

  10. 二、python沉淀之路~~字符串属性(str)

    1.capitalize的用法:即将输出字符串首字母大写 test = "heLLo" v = test.capitalize() print(v) 结果:Hello. 2.cas ...