3

如果我有多个线程试图将相同的值写入内存中的单个位置,是否可能存在竞争条件?数据会在写入过程中以某种方式损坏吗?没有前面的读取或测试条件,只有写入...

编辑:澄清一下,我在 GPU 上计算一个点积。我正在使用多个线程来计算单个产品(每行/列元素一个线程)并将它们保存到内存中的临时位置。然后我需要对这些中间产品求和并保存结果。

我正在考虑让所有线程单独执行此求和/存储操作,因为在 GPU 上进行分支会损害性能。(您会认为无论是由单个线程还是所有线程完成的求和/存储都应该花费相同的时间,但是我已经对此进行了测试,并且性能受到了很小的影响。)所有线程都将获得相同的总和,但是当他们每个人都尝试将答案写入内存中的同一位置时,我担心竞争条件。在我所做的有限测试中,一切似乎都很好,但我仍然很紧张......

4

4 回答 4

4

在大多数平台上的大多数线程标准下,这只是被禁止或未定义的。也就是说,你不允许这样做,如果你这样做,任何事情都可能发生。

像 C 和 C++ 这样的高级语言编译器可以自由地优化代码,前提是你不会做任何你不允许做的事情。因此,“只写”操作可能不是这样的事情。如果您i = 1;使用 C 或 C++ 编写,编译器可以自由生成与您编写i = 0; i++;. 类似的混淆优化确实发生在现实世界中。

相反,请遵循您使用的任何线程模型的规则,以使用适当的同步原语。如果您的平台提供它们,请使用适当的原子操作。

于 2012-11-14T17:22:25.763 回答
2

多个线程在 CUDA 中写入单个(可能是共享的或全局的)内存位置没有问题,即使是“同时”,即从同一行代码。

如果您关心写入的顺序,那么这是一个问题,因为 CUDA 不保证顺序,因为多个线程对同一内存位置执行相同的写入操作。如果这是一个问题,您应该使用原子或其他重构代码的方法来解决它。(听起来这对你来说不是问题。)

据推测,正如另一位响应者所说,您在某些时候关心结果。因此,有必要设置某种屏障,无论是显式的(例如,__synchthreads(),用于使用共享内存的块中的多个线程)或隐式的(例如,内核的结尾,用于多个线程写入全局内存中的位置) ) 在您阅读该位置并期待一个合理的结果之前。请注意,这些并不是唯一可以为您提供合理结果的屏障方法,仅举两个例子。可以利用 Warp 同步行为或其他聪明的编码技术来确保在写入集合之后读取的完整性。

于 2012-11-16T16:18:58.200 回答
2

尽管从表面上看答案似乎是否定的,但没有竞争条件,但答案更加微妙。Boris 是对的,在某些 32 位架构上,存储 64 位长或地址可能需要两次操作,因此可能会在无效状态下读取。这可能很难重现,因为内存页面通常是更新的,而 long 永远不会跨越内存页面。

但是,更重要的问题是您需要意识到,如果没有内存同步,则无法保证线程何时会看到更新的值。线程可能会运行很长时间,从内存中读取过期值。它不会是无效值,但不会是最近写入的值。这可能不会特别导致“竞争条件”,但可能会导致您的程序以意想不到的方式执行。

此外,尽管您说它是“只写”,但显然有人正在读取该值,否则将没有理由执行更新。代码的哪一部分正在读取该值的详细信息将更好地告知我们不同步的只写是否真正安全。

于 2012-11-14T17:24:21.923 回答
1

如果只写操作显然不是原子的,那么会有一段时间另一个线程可能会观察到处于损坏状态的数据。

例如写入 64 位整数,它们存储为一对 32 位整数。

线程A——刚写完高位字,线程B刚写完低位字,准备设置高位字;

线程 C 可能会看到整数由线程 B 写入的低位字和线程 A 写入的高位字组成。

PS这个问题很笼统,实际结果将取决于环境(语言)的内存模型和底层处理器架构(硬件)。

于 2012-11-14T17:15:24.440 回答