是否可以为只写操作设置竞争条件?

时间:2012-11-14 17:08:18

标签: concurrency cuda gpgpu

如果我有几个线程试图将相同的值写入内存中的单个位置,是否可能出现竞争条件?在写入期间,数据是否会以某种方式被破坏?没有先前的读取或测试条件,只有写...

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

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

4 个答案:

答案 0 :(得分:4)

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

C和C ++等高级语言编译器可以根据您不会做任何不允许做的事情来自由优化代码。因此,“只写”操作可能会变成这样的事情。如果在C或C ++中编写i = 1;,编译器可以自由生成与编写i = 0; i++;时相同的代码。类似的混淆优化确实在现实世界中发生。

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

答案 1 :(得分:2)

虽然从表面上看答案似乎是否定的,但没有竞争条件,答案有点细微差别。 Boris是正确的,在某些32位体系结构中,存储64位长或地址可能需要两次操作,因此可能会以无效状态读取。这可能很难重现,因为内存页面通常是更新的,而long则永远不会跨越内存页面。

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

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

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:1)

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

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

线程A - 刚刚写完高阶字,而线程B刚刚写完低阶字,并将设置高阶字;

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

P.S。这个问题非常通用,实际结果将取决于环境(语言)的内存模型和底层处理器架构(硬件)。