繁体   English   中英

C ++编译器如何确保在不同的线程上安全地使用不同但相邻的内存位置?

[英]What does the C++ compiler do to ensure that different but adjacent memory locations are safe to be used on different threads?

可以说我有一个结构:

struct Foo {
  char a;  // read and written to by thread 1 only
  char b;  // read and written to by thread 2 only
};

根据我的理解,当两个线程在两个不同的内存位置上运行时,C ++标准保证了上述的安全性。

我想尽管如此,因为char a和char b属于同一个缓存行,所以编译器必须进行额外的同步。

这到底发生了什么?

这取决于硬件。 在我熟悉的硬件上,C ++不需要做任何特殊的事情,因为从硬件角度来看,即使在缓存行上访问不同的字节也是“透明地”处理的。 从硬件上看,这种情况并没有什么不同

char a[2];
// or
char a, b;

在上面的例子中,我们讨论的是两个相邻的对象,保证可以独立访问。

但是,我把'透明'放在引号中是有原因的。 当你真的有这样的情况时,你可能会因为“错误共享”而遭受(性能方面的) - 当两个(或更多)线程同时访问相邻内存并且最终被缓存在几个CPU的缓存中时会发生这种情况。 这导致持续的高速缓存失效。 在现实生活中,应该注意尽可能防止这种情况发生。

正如其他人所解释的那样,在通用硬件上没有特别之处 但是,有一个问题:编译器必须避免执行某些优化,除非它可以证明其他线程不访问有问题的内存位置,例如:

std::array<std::uint8_t, 8u> c;

void f()
{
    c[0] ^= 0xfa;
    c[3] ^= 0x10;
    c[6] ^= 0x8b;
    c[7] ^= 0x92;
}

这里,在单线程内存模型中,编译器可以发出如下代码(伪程序集;假设小端硬件):

load r0, *(std::uint64_t *) &c[0]
xor r0, 0x928b0000100000fa
store r0, *(std::uint64_t *) &c[0]

在普通硬件上,这可能比单个字节更快。 但是,它在索引1,2,4和5处读取和写入c的未受影响(和未提及的)元素。如果其他线程同时写入这些存储器位置,则可以覆盖这些更改。

因此,在多线程内存模型中,这些优化通常无法使用。 只要编译器仅执行匹配长度的加载和存储,或者仅在没有间隙时合并访问(例如,仍然可以合并对c[6]c[7]的访问),硬件通常已经提供了必要的保证正确执行。

(也就是说,有一些架构具有弱且违反直觉的内存顺序保证,例如DEC Alpha不像其他架构那样跟踪指针作为数据依赖关系,因此有必要在某些架构中引入显式内存屏障的情况下,在低级别的代码,有是一个有些知名的小咆哮由Linus Torvalds在这个问题上 。但是,标准的C ++实现预期从这样的问题保护你。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM