CUDA异步并发之CUDA流详解

2023-11-19

CUDA中得异步并发

在这里插入图片描述

CUDA 将以下操作公开为可以彼此同时操作的独立任务:

  • 在主机上计算;
  • 设备上的计算;
  • 从主机到设备的内存传输;
  • 从设备到主机的内存传输;
  • 在给定设备的内存中进行内存传输;
  • 设备之间的内存传输。

这些操作之间实现的并发级别将取决于设备的功能和计算能力,如下所述。

主机和设备之间的并发执行

在设备完成请求的任务之前,异步库函数将控制权返回给宿主线程,从而促进了主机的并发执行。使用异步调用,许多设备操作可以在适当的设备资源可用时排队,由CUDA驱动程序执行。这减轻了主机线程管理设备的大部分责任,让它自由地执行其他任务。以下设备操作对主机是异步的:

  • 内核启动;
  • 内存复制在单个设备的内存中;
  • 从主机到设备内存拷贝的内存块大小不超过64kb的;
  • 由带有Async后缀的函数执行的内存拷贝;
  • 内存设置函数调用。
    程序员可以通过将CUDA_LAUNCH_BLOCKING环境变量设置为1来全局禁用系统上运行的所有CUDA应用程序的内核启动的异步性。此特性仅用于调试目的,不应用作使生产软件可靠运行的一种方法。

如果通过分析器(Nsight、Visual Profiler)收集硬件计数器,则内核启动是同步的,除非启用了并发内核分析。如果异步内存复制涉及非页面锁定的主机内存,它们也将是同步的。

并行执行内核

某些计算能力 2.x 及更高版本的设备可以同时执行多个内核。 应用程序可以通过检查 concurrentKernels 设备属性(请参阅设备枚举)来查询此功能,对于支持它的设备,该属性等于 1。

设备可以同时执行的内核启动的最大数量取决于其计算能力,并在表15 中列出。

来自一个 CUDA 上下文的内核不能与来自另一个 CUDA 上下文的内核同时执行。

使用许多纹理或大量本地内存的内核不太可能与其他内核同时执

数据传输和内核执行的重叠

一些设备可以在内核执行的同时执行与 GPU 之间的异步内存复制。 应用程序可以通过检查 asyncEngineCount 设备属性(请参阅设备枚举)来查询此功能,对于支持它的设备,该属性大于零。 如果复制中涉及主机内存,则它必须是页锁定的。

还可以与内核执行(在支持 concurrentKernels 设备属性的设备上)或与设备之间的拷贝(对于支持 asyncEngineCount 属性的设备)同时执行设备内复制。 使用标准内存复制功能启动设备内复制,目标地址和源地址位于同一设备上。

并行数据传输

某些计算能力为 2.x 及更高版本的设备可以重叠设备之间的数据拷贝。 应用程序可以通过检查 asyncEngineCount 设备属性(请参阅设备枚举)来查询此功能,对于支持它的设备,该属性等于 2。 为了重叠,传输中涉及的任何主机内存都必须是页面锁定的。

CUDA流

应用程序通过流管理上述并发操作。 流是按顺序执行的命令序列(可能由不同的主机线程发出)。 另一方面,不同的流可能会彼此乱序或同时执行它们的命令; 不能保证此行为,因此不应依赖其正确性(例如,内核间通信未定义)。 当满足命令的所有依赖项时,可以执行在流上发出的命令。 依赖关系可以是先前在同一流上启动的命令或来自其他流的依赖关系。 同步调用的成功完成保证了所有启动的命令都完成了。

创建与销毁

流是通过创建一个流对象并将其指定为一系列内核启动和主机 <-> 设备内存拷贝的流参数来定义的。 以下代码示例创建两个流并在锁页内存中分配一个浮点数组 hostPtr

cudaStream_t stream[2];
for (int i = 0; i < 2; ++i)
    cudaStreamCreate(&stream[i]);
float* hostPtr;
cudaMallocHost(&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() 时设备仍在流中工作,则该函数将立即返回,并且一旦设备完成流中的所有工作,与流关联的资源将自动释放。

默认流

未指定任何流参数或等效地将流参数设置为零的内核启动和主机 <-> 设备内存拷贝将发布到默认流。因此它们按顺序执行。

对于使用 --default-stream per-thread 编译标志编译的代码(或在包含 CUDA 头文件(cuda.h 和 cuda_runtime.h)之前定义 CUDA_API_PER_THREAD_DEFAULT_STREAM 宏),默认流是常规流,并且每个主机线程有自己的默认流。

注意:当代码由 nvcc 编译时,#define CUDA_API_PER_THREAD_DEFAULT_STREAM 1 不能用于启用此行为,因为 nvcc 在翻译单元的顶部隐式包含 cuda_runtime.h。在这种情况下,需要使用 --default-stream 每个线程编译标志,或者需要使用 -DCUDA_API_PER_THREAD_DEFAULT_STREAM=1 编译器标志定义 CUDA_API_PER_THREAD_DEFAULT_STREAM 宏。

对于使用 --default-stream legacy 编译标志编译的代码,默认流是称为 NULL 流的特殊流,每个设备都有一个用于所有主机线程的 NULL 流。 NULL 流很特殊,因为它会导致隐式同步,如隐式同步中所述。

对于在没有指定 --default-stream 编译标志的情况下编译的代码, --default-stream legacy 被假定为默认值。

显式同步

有多种方法可以显式地同步流。

cudaDeviceSynchronize() 一直等待,直到所有主机线程的所有流中的所有先前命令都完成。

cudaStreamSynchronize() 将流作为参数并等待,直到给定流中的所有先前命令都已完成。 它可用于将主机与特定流同步,允许其他流继续在设备上执行。

cudaStreamWaitEvent() 将流和事件作为参数(有关事件的描述,请参阅事件),并在调用 cudaStreamWaitEvent() 后使添加到给定流的所有命令延迟执行,直到给定事件完成。

cudaStreamQuery() 为应用程序提供了一种方法来了解流中所有前面的命令是否已完成。

隐式同步

如果主机线程在它们之间发出以下任一操作,则来自不同流的两个命令不能同时运行:

  • 页面锁定的主机内存分配,
  • 设备内存分配,
  • 设备内存设置,
  • 两个地址之间的内存拷贝到同一设备内存,
  • 对 NULL 流的任何 CUDA 命令,
  • 计算能力 3.x 计算能力 7.x 中描述的 L1/共享内存配置之间的切换。

对于支持并发内核执行且计算能力为 3.0 或更低的设备,任何需要依赖项检查以查看流内核启动是否完成的操作:

  • 仅当从 CUDA 上下文中的任何流启动的所有先前内核的所有线程块都已开始执行时,才能开始执行;
  • 阻止所有以后从 CUDA 上下文中的任何流启动内核,直到检查内核启动完成。

需要依赖检查的操作包括与正在检查的启动相同的流中的任何其他命令以及对该流的任何 cudaStreamQuery() 调用。 因此,应用程序应遵循以下准则来提高并发内核执行的潜力:

  • 所有独立操作都应该在依赖操作之前发出,
  • 任何类型的同步都应该尽可能地延迟。

重叠行为

两个流之间的执行重叠量取决于向每个流发出命令的顺序以及设备是否支持数据传输和内核执行的重叠(请参阅数据传输和内核执行的重叠)、并发内核执行( 请参阅并发内核执行)和并发数据传输(请参阅并发数据传输)。

例如,在设备不支持并行数据传输,这两个流的代码示例创建和销毁不重叠,因为由stream[1]发起的内存复制会在stream[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]);

那么在stream[1]上从主机到设备的内存复制 与stream[0]上内核启动重叠。

在支持并发数据传输的设备上,Creation 和 Destruction 的代码示例的两个流确实重叠:在stream[1]上从主机到设备的内存复制 与在stream[0]上从设备到主机的内存复制甚至在stream[0]上内核启动(假设设备支持数据传输和内核执行的重叠)。但是,对于计算能力为 3.0 或更低的设备,内核执行不可能重叠,因为在stream[0]上从设备到主机的内存复制之后,第二次在stream[1]上内核启动,因此它被阻塞,直到根据隐式同步,在stream[0]上第一个内核启动已完成。如果代码如上重写,内核执行重叠(假设设备支持并发内核执行),因为在stream[0]上从设备到主机的内存复制之前,第二次在stream[1]上内核启动被。但是,在这种情况下,根据隐式同步,在stream[0]上从设备到主机的内存复制仅与在stream[1]上内核启动的最后一个线程块重叠,这只能代表总数的一小部分内核的执行时间。

Host函数(回调)

运行时提供了一种通过 cudaLaunchHostFunc() 在任何点将 CPU 函数调用插入到流中的方法。 在回调之前向流发出的所有命令都完成后,在主机上执行提供的函数。

以下代码示例在向每个流发出主机到设备内存副本、内核启动和设备到主机内存副本后,将主机函数 MyCallback 添加到两个流中的每一个。 每个设备到主机的内存复制完成后,该函数将在主机上开始执行。

void CUDART_CB MyCallback(cudaStream_t stream, cudaError_t status, void *data){
    printf("Inside callback %d\n", (size_t)data);
}
...
for (size_t i = 0; i < 2; ++i) {
    cudaMemcpyAsync(devPtrIn[i], hostPtr[i], size, cudaMemcpyHostToDevice, stream[i]);
    MyKernel<<<100, 512, 0, stream[i]>>>(devPtrOut[i], devPtrIn[i], size);
    cudaMemcpyAsync(hostPtr[i], devPtrOut[i], size, cudaMemcpyDeviceToHost, stream[i]);
    cudaLaunchHostFunc(stream[i], MyCallback, (void*)i);
}

在主机函数之后在流中发出的命令不会在函数完成之前开始执行。

在流中的主机函数不得进行 CUDA API 调用(直接或间接),因为如果它进行这样的调用导致死锁,它可能最终会等待自身。

流优先级

可以在创建时使用 cudaStreamCreateWithPriority() 指定流的相对优先级。 可以使用 cudaDeviceGetStreamPriorityRange() 函数获得允许的优先级范围,按 [最高优先级,最低优先级] 排序。 在运行时,高优先级流中的待处理工作优先于低优先级流中的待处理工作。

以下代码示例获取当前设备允许的优先级范围,并创建具有最高和最低可用优先级的流。

// get the range of stream priorities for this device
int priority_high, priority_low;
cudaDeviceGetStreamPriorityRange(&priority_low, &priority_high);
// create streams with highest and lowest available priorities
cudaStream_t st_high, st_low;
cudaStreamCreateWithPriority(&st_high, cudaStreamNonBlocking, priority_high);
cudaStreamCreateWithPriority(&st_low, cudaStreamNonBlocking, priority_low);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA异步并发之CUDA流详解 的相关文章

  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • 类型约束

    我有以下类层次结构 class Header IEnumerable
  • 识别 Visual Studio 中的重载运算符 (c++)

    有没有办法使用 Visual Studio 快速直观地识别 C 中的重载运算符 在我看来 C 中的一大问题是不知道您正在使用的运算符是否已重载 Visual Studio 或某些第三方工具中是否有某些功能可以自动突出显示重载运算符或对重载运
  • IQueryable 单元或集成测试

    我有一个 Web api 并且公开了一个端点 如下所示 api 假期 name name 这是 Web api 的控制器 get 方法 public IQueryable
  • 在视口中查找 WPF 控件

    Updated 这可能是一个简单或复杂的问题 但在 wpf 中 我有一个列表框 我用一个填充数据模板从列表中 有没有办法找出特定的数据模板项位于视口中 即我已滚动到其位置并且可以查看 目前我连接到了 listbox ScrollChange
  • Django 2、python 3.4 无法解码 urlsafe_base64_decode(uidb64)

    我正在尝试通过电子邮件激活用户 电子邮件有效 编码有效 我使用了 django1 11 中的方法 该方法运行成功 在 Django 1 11 中 以下内容成功解码为 28 其中 uidb64 b Mjg force text urlsafe
  • 为什么这个二维指针表示法有效,而另一个则无效[重复]

    这个问题在这里已经有答案了 这里我编写了一段代码来打印 3x3 矩阵的对角线值之和 这里我必须将矩阵传递给函数 矩阵被传递给指针数组 代码可以工作 但问题是我必须编写参数的方式如下 int mat 3 以下导致程序崩溃 int mat 3
  • C++ int 前面加 0 会改变整个值

    我有一个非常奇怪的问题 如果我像这样声明一个 int int time 0110 然后将其显示到控制台返回的值为72 但是当我删除前面的 0 时int time 110 然后控制台显示110正如预期的那样 我想知道两件事 首先 为什么它在
  • C++ 中的双精度型数字

    尽管内部表示有 17 位 但 IEE754 64 位 浮点应该正确表示 15 位有效数字 有没有办法强制第 16 位和第 17 位为零 Ref http msdn microsoft com en us library system dou
  • 等待 IAsyncResult 函数直至完成

    我需要创建等待 IAsyncResult 方法完成的机制 我怎样才能做到这一点 IAsyncResult result contactGroupServices BeginDeleteContact contactToRemove Uri
  • Unity:通过拦截将两个接口注册为一个单例

    我有一个实现两个接口的类 我想对该类的方法应用拦截 我正在遵循中的建议Unity 将两个接口注册为一个单例 https stackoverflow com questions 1394650 unity register two inter
  • WebBrowser.Print() 等待完成。 。网

    我在 VB NET 中使用 WebBrowser 控件并调用 Print 方法 我正在使用 PDF 打印机进行打印 当调用 Print 时 它不会立即启动 它会等到完成整个子或块的运行代码 我需要确保我正在打印的文件也完整并继续处理该文件
  • OpenGL:仅获取模板缓冲区而没有深度缓冲区?

    我想获取一个模板缓冲区 但如果可能的话 不要承受附加深度缓冲区的开销 因为我不会使用它 我发现的大多数资源表明 虽然模板缓冲区是可选的 例如 排除它以利于获得更高的深度缓冲区精度 但我还没有看到任何请求并成功获取仅 8 位模板缓冲区的代码
  • 如何创建用于霍夫曼编码和解码的树?

    对于我的作业 我将对霍夫曼树进行编码和解码 我在创建树时遇到问题 并且陷入困境 不要介意打印语句 它们只是让我测试并查看函数运行时的输出是什么 对于第一个 for 循环 我从主块中用于测试的文本文件中获取了所有值和索引 在第二个 for 循
  • 是否可以使用 Anaconda 包作为 Google Cloud Functions 的依赖项?

    我正在使用 Python 运行时编写 Google Cloud Function 我需要包含一些无法使用的依赖项pip 如文档中所述here https cloud google com functions docs writing spe
  • python中有没有一种方法可以将存储在列表中的正则表达式模式列表应用到单个字符串?

    我有一个正则表达式模式列表 存储在列表类型中 我想将其应用于字符串 有谁知道一个好方法 将列表中的每个正则表达式模式应用于字符串 和 如果匹配 则调用与列表中该模式关联的不同函数 如果可能的话我想用 python 来做这件事 提前致谢 im
  • Python 相当于 Scala 案例类

    Python 中是否有与 Scala 的 Case Class 等效的东西 就像自动生成分配给字段而无需编写样板的构造函数一样 当前执行此操作的现代方法 从 Python 3 7 开始 是使用数据类 https www python org
  • 两种 ODE 求解器之间的差异

    我想知道 两者之间有什么区别ODEINT and solve ivp用于求解微分方程 它们之间有什么优点和缺点 f1 solve ivp f 0 1 y0 y0 is the initial point f2 odeint f y0 0 1
  • 使用 pandas 单元格中列表的长度选择行[重复]

    这个问题在这里已经有答案了 我有一张表 df a b c 1 x y x 2 x z c d 3 x t e f g 只是想知道如何使用 c 列的长度选择行 such as df loc len df c gt 1 我知道这是不对的 正确的
  • 如何使用 C++11 using 语法键入定义函数指针?

    我想写这个 typedef void FunctionPtr using using 我该怎么做呢 它具有类似的语法 只不过您从指针中删除了标识符 using FunctionPtr void 这是一个Example http ideone

随机推荐

  • Ansible Ad-Hoc与常用模块

    ansible 执行结果信息 各颜色说明 ansible Ad Hoc 说明 ansible 如何查看帮助文档与常用模块详解 主机规划 主机名称 操作系统版本 内网IP 外网IP 模拟 安装软件 ansi manager CentOS7 5
  • 【Python】自动化构建项目结构样式

    引言 在使用Python或者其它编程语言的项目时候 编写README md 往往是不可或缺的 而在README md 中 关于项目结构的样式展示 这个是可选的 不展示也无伤大雅 但有展示的话 有以下优点 提供清晰的项目导航 包含项目结构的文
  • A deep learning approach to detection of splicing and copy-move forgeries in images

    https github com kPsarakis Image Forgery Detection CNNhttps github com kPsarakis Image Forgery Detection CNN 代码是结合代尔夫特理工
  • Python报错: Using TensorFlow backend

    Python报错 Using TensorFlow backend 环境 系统 win10 pycharm2017 问题描述 导入keras库运行时 Python总是出现Using TnesorFlow backend报错 解决过程 网上有
  • android-smart-image-view源码分析,android性能优化推荐书

    三 源码分析 从github上clone该项目 可以看到整个项目的代码只包含7个Java源文件 同时 还可进行扩展 方便使用者根据实际图片的来源进行扩展 我们来看看Class逻辑图 上面有提到 SmartImageView继承自ImageV
  • python 从外部引入变量并运行该程序

    1 python程序部分 import argparse FLAGS tf app flags FLAGS office31 flags train parser argparse ArgumentParser parser add arg
  • java自动化测试框架基础eclipse+maven配置

    java自动化测试框架基础eclipse maven配置 文章目录 java自动化测试框架基础eclipse maven配置 一 maven安装配置 二 eclipse中使用maven 一 maven安装配置 Maven是一个项目构建和管理
  • IDEA安装及配置

    目录 下载与安装 IDEA文件目录介绍 IDEA优化配置 提高启动和运行 下载与安装 IDEA下载网址 JetBrains Essential tools for software developers and teams 在官网中找到自己
  • make: *** No rule to make target 错误原因、分析和解决办法

    问题描述 在用codewarrior编译的时候 遇到编译器报如下错误 mingw32 make No rule to make target D CW Workspace Renalt PBG BOOT Project Settings L
  • 2021常见面试题汇总(持续更新)

    2021常见面试题汇总 1 Valatile的定义和使用 1 1 可见性 1 2 有序性 2 syc1 8之后有什么区别 3 synchronized和Lock的区别 4 redis如何进行大key或value值删除 5 redis如何进行
  • SQL server 查询语句大全

    在 SQL Server 中 查询语句是最常用的语句类型 用于从数据库中提取有用的信息 SQL Server 中常用的查询语句有 SELECT FROM WHERE GROUP BY HAVING 和 ORDER BY 1 SELECT S
  • 【数电】常用时序逻辑电路模块总结

    文章目录 同步置零和异步置零 同步预置数和异步预置数 一 移位寄存器 I D触发器构成的4位移位寄存器 II 双向移位寄存器 74HC194 二 计数器 I 同步计数器 i 同步二进制计数器 1 同步二进制加法计数器 74161 2 同步二
  • iOS中自动消失提示框的实现

    iOS中自动消失提示框的实现 添加一个提示框 UIAlertView alert UIAlertView alloc initWithTitle 提示 message 你很漂亮 delegate self cancelButtonTitle
  • chrome浏览器安装失败,已解决(方便)

    原因分析 如果是第一次安装 一般都会安装成功 倘若报错后安装失败 说明之前电脑上存在Google Chrome 谷歌浏览器 安装的残余 导致再次安装时 无法将安装的数据正常的写入注册表 因为在软件安装过程中 都会将必要的文件添加到注册表中
  • 大数据学习之Scala——02Scala基础

    一 杂项 1 Scala语言输出的三种方式 字符串通过 号连接 类似java printf用法 类似C语言 字符串通过 传值 格式化输出 字符串插值 通过 引用 类似PHP println name name age age url url
  • dosbox中out of memory_在Rust中实现goto逻辑

    众所周知 在Rust中是没有goto表达式的 最近在 试着用Rust练习翻新一些古代陈旧代码 结果这堆古代的pascal代码中就有很多goto语句 于是写了几个宏来模拟了一下 在这里也写一篇文章介绍一下 希望给大家在思路上有所帮助 如果不想
  • ​​PMP项目管理—第3章 项目经理的角色。

    PMBOK项目管理知识体系指南 PMP项目管理学习笔记 总 第1章 引论 第2章 项目运行环境 第3章 项目经理的角色 第4章 项目整合管理 第5章 项目范围管理 第6章 项目进度管理 第7章 项目成本管理 第8章 项目质量管理 第9章 项
  • c/c++入门教程 - 1.基础c/c++ - 1.0 Visual Studio 2019安装环境搭建

    推荐视频课程 https www bilibili com video BV1et411b73Z p 2 已投币三连 b站果然是个学习的网站 本来是想在linux环境下运行QT 于是先学了几个月linux嵌入式驱动开发 后来发现太底层了 与
  • 【FPGA】面试问题及答案整理合集

    面试问题及答案整理合集 1 硬件描述语言和软件编程语言的区别 2 FPGA选型问题 3 建立时间和保持时间问题 3 亚稳态问题 4 竞争和冒险问题 5 乒乓操作问题 6 同步和异步逻辑电路 7 同步复位和异步复位 8 MOORE 与 MEE
  • CUDA异步并发之CUDA流详解

    CUDA中得异步并发 CUDA 将以下操作公开为可以彼此同时操作的独立任务 在主机上计算 设备上的计算 从主机到设备的内存传输 从设备到主机的内存传输 在给定设备的内存中进行内存传输 设备之间的内存传输 这些操作之间实现的并发级别将取决于设