在对全局内存进行读写时,提供哪些撕裂相关的保证?

编程语言 2026-07-10

假设有10个写线程和10个读线程同时从全局内存中存放的同一个32位整数进行读写,发生在设备代码中。对读写操作不产生tearing(内存撕裂)的保障是否存在?如果该整数是天然对齐的呢?

我猜如果整型对齐,应该就不会出现撕裂,但我找不到权威来源来支持这一点。

若有一般性的解答会很受欢迎,但如果这点对你有影响的话,我使用的是Pascal架构的GPU(是的,它很老)。

解决方案

你的问题缺少关于你是使用原子操作、volatile,还是普通的 int 变量的信息。无论如何,基本上有四种理解方式:

CUDA C++ memory model

当前的内存模型在编程指南中有所描述,基于C++11引入的C++内存模型。它的差异与本问题无关。关于该模型有大量的文档、描述与讨论。我不打算在这里展开过多细节。相关章节 部分引用了如下内容:

程序的执行包含数据竞争,当它包含两个潜在并发的冲突操作,且至少有一个不是原子操作,且两者之间没有先发生前关系,除了下文对信号处理程序描述的特殊情况。任何此类数据竞争都会导致未定义行为。

这涵盖了普通的 int 以及 volatile int。它们可能会经历撕裂,也可能崩溃、读取任意值,或执行实现者想要或需要的任何其他操作。简而言之,就是未定义。原子操作不是未定义——这是它们的全部意义——但在你的情境下仍然会是数据竞争;若不建立任何先前关系(happens-before),任意读者都可能读到任意写者写入的值或初始值。它们不能读取无效的值,例如撕裂。

Old CUDA C++ memory model

在CUDA采用C++11模型之前,模型比较非正式,基于 volatile 的原子访问以及用于内存屏障、同步和原子读-改-写的内建函数。编程指南警告不要为此使用它们,但我认为对于下面基于PTX语义的简单情形仍然适用。

volatile-限定对象的读写不是原子性的,并被编译成一个或多个volatile指令,这些指令不能保证:

  • 内存操作的有序性,或
  • 硬件执行的内存操作数量与PTX指令数量相匹配。

从技术上讲,这确实允许撕裂。就实际情况而言,对于一个简单、天然对齐的 float,你几乎不可能看到撕裂。

PTX memory model

PTX的内存模型与C++的不同,甚至有它自己的正式验证论文,由 [Daniel Lustig等人:A Formal Analysis of the NVIDIA PTX Memory Consistency Model](链接)提出。

它的 原子性公理 保证:

冲突的 morally strong 操作以单拷贝原子性执行

单拷贝原子性被进一步解释为本质上的一致性排序加上无撕裂。那和我上面对C++风格原子操作的描述是一样的。[morally strong] 到底是什么意思?

两个操作相对于彼此若满足以下所有条件,则被称为 morally strong

  1. 这两个操作在程序序列中相关(即由同一线程执行),或者每个操作都是 strong,并指定包含执行对方操作的线程的作用域。
  2. 两个操作都通过同一个代理执行。
  3. 如果两者都是内存操作,则它们完全重叠。

第3 点是显而易见的,因为你读写的是完全相同的浮点数。第2 点成立,因为所有操作都是同步的(没有异步拷贝)并且通过全局内存。第1 点只有在内存操作是 strong 时才能成立。

[strong 是什么意思?] 好吧,若看这个列表,基本上就是所有原子操作(松弛、释放-获取等)再加上 volatilemmio。这也是为什么我认为只要C++编译器对volatile访问保持友好并把每次volatile访问都转换成一个单独的volatile PTX操作,旧式C++风格的 volatile 仍然会可用的原因。

strong 的反义词是 weak;而任何常规的内存操作都是 weak。对于它们,在你的情景下没有定义的行为,意味着是未定义的;与C++中的情况相同。

硬件层面

CUDA硬件有缓存,当前我记得大概是128字节缓存行,以及全局内存的32字节内存事务。对于一个简单的4 字节数据类型且4 字节对齐,在这样的硬件上几乎不可能产生撕裂。与x86和 ARM64不同,错位的加载和存储也不被支持,因此你也无法通过这一点造成撕裂。

对于全局内存,你需要更大的结构体,或任何需要拆分成多次内存操作的情况,例如 float3。对于共享内存,也许8 字节类型也可能产生撕裂;还不太确定。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章