`is_always_lock_free`在macOS上返回`true`,但`is_lock_free()`却返回`false`,为什么?

29

我正在尝试使用 C++ 原子变量的 std::atomic<T>::is_always_lock_freestd::atomic<T>::is_lock_free 进行实验。

我编写了一个简单的结构体 A,想知道原子版本的 A 是否是无锁的:

#include <iostream>
#include <atomic>

using namespace std;

struct A {
  int x;
  int y;
  int z;
};

int main() {
  atomic<A> b;

  cout << boolalpha;
  cout << "b.is_always_lock_free = " << b.is_always_lock_free << endl;
  cout << "b.is_lock_free = " << b.is_lock_free() << endl;

  return 0;
}

x86-64 Linux 上,我使用 g++ 9.4.0 和 C++17 进行编译,输出正常:

b.is_always_lock_free = false
b.is_lock_free = false

然而,我也在我的Mac(ARM64)上使用clang++ 16.0.0编译了它,输出结果很奇怪:

b.is_always_lock_free = true
b.is_lock_free = false

为什么 is_always_lock_free = trueis_lock_free = false?如果它总是可以无锁,为什么 b 不是无锁的?

2
至少在MacOS上使用clang 14,这种类型实际上是无锁的,并且加载和存储操作会给你一个简单的ldp/stp(在ARMv8.4上是原子操作)。 - undefined
10
提一下为什么GCC在x86-64上对于大于8字节但小于等于16字节的类型报告非无锁(lock-free)情况,即使使用了-march=native选项(在启用了-mcx16的机器上):GCC7避免内联lock cmpxchg16b指令,并报告非无锁,是因为它没有预期的读取侧扩展性(read-side scaling),即读者相互竞争。这可能会改变,一旦GCC开始利用Intel最近几年文档化的16字节加载/存储原子性保证,以支持具备AVX功能标志的Intel CPU。更多详情请参考:https://gcc.gnu.org/ml/gcc-patches/2017-01/msg02344.html。 - undefined
1
就记录而言,C++20标准中的第31.8.2节规定了is_lock_free成员函数的返回值与相同类型的is_always_lock_free的值一致。 - undefined
@helix 在具体平台/实现中,情况可能会有所不同,其中突然的原子需求大于此单元中std::atomic<T>的对齐。请注意,is_always_lock_free对于所有编译单元都是一个常量,并且显然在翻译开始之前由clang确定,但是模块可以使用不同的设置进行编译,在这种特殊情况下(局部变量)不会造成问题。但是,如果这是一个共享变量,可能会有问题。is_lock_free()是一个函数。我想知道如果变量是全局的并且在其他单元中,结果是否会相同。 - undefined
有人能够复现吗?我没有ARM进行测试,但是汇编代码,无论是原始版本还是我简化的版本,都显示b.is_always_lock_free解析为0。https://godbolt.org/z/xP3z54acz - undefined
1
@yyyy:是的,我可以确认在MacOS 13.6上的M1 Pro笔记本上,使用苹果的命令行开发工具中的clang++ 15.0.0,并使用-std=c++20选项,输出结果为true / false。Godbolt在这里无法提供帮助,因为它大多数情况下在Linux环境下编译,而这个问题来自于MacOS系统库。 - undefined
1个回答

5
这是标准库libc++中的一个错误(在libstdc++中不存在)。实际上,它调用了内置函数:
使用sizeof(__cxx_atomic_impl<A>)调用atomic_always_lock_free,结果为16
而使用sizeof(A)调用atomic_is_lock_free,结果为12
因此导致结果不一致。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接