【发布时间】:2011-03-21 16:17:05
【问题描述】:
是否有任何现代通用 CPU 不安全地从不同线程同时写入数组的相邻元素?我对x86特别感兴趣。你可能会认为编译器没有做任何明显荒谬的事情来增加内存粒度,即使它在技术上符合标准。
我对编写任意大的结构感兴趣,而不仅仅是原生类型。
注意:
请不要提及虚假分享的性能问题。我很清楚这些,但它们对我的用例没有实际意义。我还意识到从阅读器以外的线程写入的数据的可见性问题。这在我的代码中得到了解决。
澄清:出现此问题是因为在某些处理器(例如,旧的 DEC Alphas)上,内存只能在字级别处理。因此,以非字长增量(例如,单个字节)写入内存实际上涉及对要写入的字节加上一些相邻字节的读取-修改-写入。为了可视化这一点,请考虑写入单个位所涉及的内容。您读入字节或字,对整个事物执行按位运算,然后将整个事物写回。因此,您不能安全地从不同线程同时写入相邻位。
理论上,编译器也可以在硬件不需要时以这种方式实现内存写入,尽管非常愚蠢。 x86 可以处理单个字节,所以这基本上不是问题,但我试图弄清楚它是否存在任何奇怪的极端情况。更一般地说,我想知道从不同线程写入数组的相邻元素是否仍然是一个实际问题,或者主要只是一个仅适用于晦涩/古老的硬件和/或非常奇怪的编译器的理论问题。
另一个编辑:这是一个很好的参考,描述了我正在谈论的问题:
http://my.safaribooksonline.com/book/programming/java/0321246780/threads-and-locks/ch17lev1sec6
【问题讨论】:
-
那我可能不明白你在问什么。您想知道将 DWORD 写入地址
0x08450380是否会影响除0x08450380到0x08450383之外的任何内存位置?如果可能的话,我会非常担心。 -
@Jim:实际上至少在一些(旧的)架构上是可能的。一些架构不能以单个字节的粒度写入。我不知道这方面的细节,但我知道,在某些 DEC alpha 上,您不能安全地从不同线程写入相邻字节,因为内存写入只能以字粒度进行。要理解这一点,请考虑尝试从不同线程写入同一字节的相邻位。您必须读入一个完整的字节,进行一些操作,然后再写回一个完整的字节。
-
我不知道,你在追鬼。是的,像 Alpha 和 Itanium 这样的内核有糟糕的内存模型。这就是我们不再使用它们的原因。
-
不确定您到底想要做什么,但是通过不同的内核写入相同的缓存行会导致性能损失。查找“虚假共享”。一般来说,您不希望不同的内核(尤其是 w/不同的 L1 缓存)修改相同的缓存行(即“写入相邻的数组元素”)
-
@bestsss:请重新阅读问题。我说过我知道这个问题。为了澄清,对相邻元素的写入将很少发生,只要它们是正确的,我不在乎它们是否效率低下。 (但是,对相距较远的元素的写入会更频繁,并且性能在这里很重要,因此锁定每次写入并不是一个好的选择。)
标签: multithreading asynchronous thread-safety x86