POD 类型上的保证原子操作在 Intel 上自然对齐

Guaranteed Atomic Operations on POD types naturally aligned on Intel

我在 Intel Xeon 32 内核上有一个 C++ 多线程应用程序 运行,使用启用了优化的 GCC 4.8.2 编译。

我有多个线程(比如 A、B、C)更新一些 POD 类型,另一个线程 D 每 K 秒读取这些变量并将其发送到 GUI。线程跨多个内核和套接字产生。写入受自旋锁保护。线程 A、B、C 对延迟敏感,其中高性能是一个关键方面。线程 D 对延迟不敏感。

类似于:

Thread A,B,C
...
// a,b,c are up to 64 bits (let's say double)
spin-lock
a = computeValue();
b = computeValue();
c = computeValue();
spin-unlock
....

Thread D
...
// a,b,c are up to 64 bits (let's say double)
currValueA = a;
currValueB = b;
currValueC = c;
sendToGui(currValueA ,currValueB ,currValueC );
....

我想利用关于保证原子操作的第 8.1.1 段 https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html,并避免放置一个锁来保护线程 D 进行的读取。

我的理解是,如果 a、b、c 自然对齐(大小不超过 64 位),线程 D 就不会读取 a、b、c 的值,而该值是在写。换句话说,写入和读取将以原子方式执行。线程 D 将读取旧值或新值。

我的理解正确吗?

我让编译器 GCC 4.8.2 负责对齐,即我不使用任何 gcc 内置指令或函数,如 std::alignas、sts::alignof 等。

我知道代码不可移植。 我宁愿不使用 std::atomic 以避免任何不必要的开销。

读取值"taken halfway during the write"只是原子性的一个方面。

现在的处理器将值保存在特定于处理器的缓存中,因此在多处理器系统上,两个不同的处理器可能对它们共享的 a 具有不同的值。将 a 标记为原子可确保不同的处理器看到 "the same" 值。

此外,编译器和处理器经常重新排序计算以更好地利用处理设施。没关系,只要那些计算的 result 没有改变。 (这是 C++ 中的 "as if" 规则)。但是"isn't changed"指的是单线程内执行。当多个线程处理同一个对象时,在单个线程中工作的优化不一定有效。通常,您不希望您的单线程代码由不进行常见优化的偏执编译器编译,因为它们可能会破坏多线程代码。相反,将一个对象标记为原子表示编译器应该非常小心它移动的东西,因为该对象的值可以在幕后被其他代码更改。

所以你有一个选择:手工编写你的代码并希望你做对了,或者接受 atomic 库的作者可能比你更了解你的目标系统上的原子性,并且可能会做得更好。