torch.Tensor.record_stream¶
- Tensor.record_stream(stream)¶
将张量标记为已由此流使用。当张量被释放时,确保张量内存在
stream
上排队的释放时所有工作完成之前不会被重用于另一个张量。注意
缓存分配器仅了解分配张量的流。由于这种感知,它已经正确地管理了仅在一个流上的张量的生命周期。但是,如果张量在与源流不同的流上使用,分配器可能会意外地重用内存。调用此方法可以让分配器知道哪些流使用了该张量。
警告
此方法最适合用于您提供一个在辅助流上创建张量的函数,并希望用户能够使用该张量而无需在使用时仔细考虑流安全性的用例。这些安全保证会带来一些性能和可预测性成本(类似于 GC 和手动内存管理之间的权衡),因此,如果您处于管理张量整个生命周期的环境中,您可能考虑改为手动管理 CUDA 事件,以便无需调用此方法。特别是,当您调用此方法时,在以后的分配中,分配器将轮询记录的流以查看所有操作是否已完成;您可能会与辅助流计算发生竞争,并且不确定地重用或无法重用分配的内存。
您可以安全地使用在辅助流上分配的张量而无需
record_stream()
;您必须手动确保张量的任何非创建流使用在您释放张量之前同步回创建流。由于 CUDA 缓存分配器保证内存将仅与相同的创建流一起重用,因此这足以确保对内存未来重新分配的写入将延迟到非创建流使用完成。(反直觉的是,您可能会观察到在 CPU 端我们已经重新分配了张量,即使旧张量上的 CUDA 内核仍在运行。这没关系,因为新张量上的 CUDA 操作将适当地等待旧操作完成,因为它们都在同一个流上。)具体来说,它看起来像这样
with torch.cuda.stream(s0): x = torch.zeros(N) s1.wait_stream(s0) with torch.cuda.stream(s1): y = some_comm_op(x) ... some compute on s0 ... # synchronize creation stream s0 to side stream s1 # before deallocating x s0.wait_stream(s1) del x
请注意,在决定何时执行
s0.wait_stream(s1)
时需要一些谨慎。特别是,如果我们在some_comm_op
之后立即等待,那么拥有辅助流就没有意义了;它等同于在s0
上运行some_comm_op
。相反,同步必须放置在您期望辅助流s1
已完成工作的某个适当的稍后时间点。此位置通常通过分析来确定,例如,使用torch.autograd.profiler.profile.export_chrome_trace()
生成的 Chrome 跟踪。如果您放置的等待时间过早,则s0
上的工作将阻塞,直到s1
完成,从而阻止通信和计算的进一步重叠。如果您放置的等待时间过晚,您将使用比绝对必要的更多的内存(因为您将x
保持活动时间更长)。有关如何在实践中应用此指导的具体示例,请参阅此帖子:FSDP 和 CUDACachingAllocator。