☰
CUDA双流为什么没有自动重叠:依赖、生命周期与计时
2026/10/25 17:55:02 网站建设 项目流程

创建两个CUDA stream,不会自动把串行工作变成并行。一个流必须读取另一个流产生的张量时,读操作仍要等待写入完成;内存还必须活到最后一次使用。10月的模型与硬件更新容易让讨论集中在吞吐数字上,但依赖关系没有因此消失。

PyTorch 2.14 CUDA语义文档把两件事分得很清楚:非默认流上的依赖需要显式同步;缓存分配器还需要知道张量会在哪些流上使用。wait_stream与record_stream解决的是不同问题。

一个缓冲区的两种风险

设生产流P写缓冲区B,消费流C读取B。C等待P先前排入的工作,可以避免读取未完成的数据。但如果B的Python引用很快释放,分配器并不知道C还在读,地址可能被回收用于其他任务。记录C对B的使用,是为了约束复用时机;它本身不会替代数据依赖。

以下是面向CUDA环境的最小示意。当前写作环境没有PyTorch和CUDA,故该段未运行,也没有附上推测的时间结果。

import torch p = torch.cuda.Stream() c = torch.cuda.Stream() with torch.cuda.stream(p): b = torch.randn(1024, device="cuda") b = b.square() c.wait_stream(p) with torch.cuda.stream(c): y = b.sum() b.record_stream(c) c.synchronize() print(y.item())

这里的c.wait_stream(p)等待调用时P上已排入的工作,不是让两个流永远绑定。将来继续向P写B,仍需建立新的依赖。示意中的消费者没有原地改B;多写者共享缓冲区时,还要分析读写冲突,不能只在初始化后等待一次。

把依赖先写成可检查的偏序

在没有GPU的环境里,可以验证算法要求的偏序,而不能验证驱动执行。下面的教学检查要求写入排在读取之前,并让释放动作晚于读取。

def valid(order): pos = {name: i for i, name in enumerate(order)} return pos["write"] < pos["read"] < pos["release"] assert valid(["write", "read", "release"]) assert not valid(["read", "write", "release"]) assert not valid(["write", "release", "read"]) print("dependency and lifetime are separate constraints")

这不是CUDA模拟器。真机复现还需记录PyTorch构建、CUDA与驱动版本、GPU型号、输入形状、数据传输方向、是否使用固定页内存,并通过profiler看时间线。CPU端提交很快,并不说明GPU已经完成;若用墙钟测量,计时区间两端需要明确同步,否则测到的可能只是排队时间。

两条独立计算链也未必有可观重叠:单个kernel可能已占满计算资源,内存带宽也可能成为瓶颈。应先证明依赖正确,再看时间线是否重叠,最后用包含同步边界的重复测量讨论收益。本文不提供双流加速比。

本地验证:上方各 Python 代码块独立保存后,在 Python 3.9.6 的临时目录以python3 example.py执行,退出码均为 0;输出分别为dependency and lifetime are separate constraints。数据为文中教学夹具,不是模型、GPU或线上性能测量。

文档为持续维护的2.14版本页面,不能将访问日期称为其发布日期。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询