【问题标题】:Does clang support a double-word-compare-and-exchange operation?clang 是否支持双字比较和交换操作?
【发布时间】:2013-08-25 19:44:32
【问题描述】:

我一直在阅读并编写 Anthony Williams 的书 Concurrency in Practice 中的示例,并且需要使用 -mcx16 为 gcc4.8 启用双字比较和交换,以便包含一个 int 指针的结构可以以无锁原子方式进行操作。

Clang(任何版本)是否支持 x64 上的双字比较和交换?

以下代码在没有额外编译器选项的情况下给出了 GCC4.8 和 Clang 3.3 中的链接错误:

#include <atomic>
#include <thread>

struct ReferenceCountedPointer
{
  int referenceCount;
  void* data;
};

int main()
{
  std::atomic<ReferenceCountedPointer> arcp;
  ReferenceCountedPointer rcp;

  arcp.compare_exchange_weak(rcp, rcp);

  return 0;
}

上述程序毫无意义,但说明了我看到的链接错误。

我用于 Clang 和 GCC 的编译命令是:

Clang 3.3:

clang++-mp-3.3 -std=c++11 -stdlib=libc++ CX16.cpp -o CX16

失败:

Undefined symbols for architecture x86_64:
"___atomic_compare_exchange", referenced from:
  _main in CX16-plVSvq.o
ld: symbol(s) not found for architecture x86_64

GCC4.8:

g++-mp-4.8 -std=c++11 CX16.cpp -o CX16

失败:

Undefined symbols for architecture x86_64:
"___atomic_compare_exchange_16", referenced from:
  std::atomic<ReferenceCountedPointer>::compare_exchange_weak(ReferenceCountedPointer&,        ReferenceCountedPointer, std::memory_order, std::memory_order) in ccOjp95s.o
ld: symbol(s) not found for architecture x86_64

【问题讨论】:

  • 大多数系统都提供这种东西。为什么要/需要依赖编译器功能?
  • 你总是可以使用内联汇编
  • @Macmade GCC 4.8 和 Clang 3.3 都不会在没有标志的情况下链接我的代码。我将使用最少的代码示例更新问题
  • @jcoe:如果您已经绑定到特定的处理器型号(范围),那么便携式在这里是相对的。
  • 这显然是两个库(llvm 的 libc++ 和 gnu 的 libstdc++)中的一个错误,因为它们似乎声明了一个在加载时无法解析的方法。

标签: c++ gcc clang


【解决方案1】:

这里的问题是某些 64 位处理器型号没有cmpxchg16b-mcx16 告诉编译器“我知道这个处理器支持 cmpxchg16b 指令,所以你可以生成它”。这是为了避免不支持此指令的旧 64 位处理器出现问题 - 它们会导致“非法操作码陷阱”。这与使用例如 SSE4 相同。

【讨论】:

    【解决方案2】:

    ReferenceCountedPointer 是可简单复制的,因此代码在 C++11 下有效。显然该库不符合标准。

    【讨论】:

    • @PeteBecker:我不完全确定你想说什么。要在 64 位架构中比较和交换具有一个指针和一个整数的数据结构,需要一个 16 字节的比较交换操作 - 如果不是,那么它就不是原子的。当然,可以围绕它写一些“互斥”锁或类似的东西,但我认为这不符合原子的技术定义。还是我错过了什么?
    • @jcoe - 唯一需要无锁的原子类型是std::atomic_flag。其他的可能是也可能不是,这就是为什么有...is_lock_free 函数和ATOMIC...LOCK_FREE 宏的原因。当然,如果std::atomic&lt;T&gt; 中的T 足够小,任何体面的实现都会生成无锁代码;通常这是为整数类型完成的。 Microsoft 的实现(我写的)使atomic&lt;T&gt;any T 无锁,这小到可以在没有互斥锁的情况下处理。
    • @MatsPetersson - 对于每个可简单复制的类型 T(整数类型和适当的 UDT),atomic&lt;T&gt; 是原子的。对于太大而无法在没有锁(互斥或其他)的情况下处理的类型,实现使用锁;对这些类型的操作不是无锁的,但它们仍然是原子的。
    • @jcoe - 当T 太大而无法由处理器的原子操作处理时,它不会在杂草中丢失,但仍然可以轻松复制,实现使用某种类型锁(互斥锁或其他)和memcmpmemcpy 以实现atomic&lt;T&gt; 的原子操作。
    • 澄清一下:C++11 原子类型中的“原子”意味着来自多个线程的操作不会产生数据竞争,缓存会更新,并且编译器不会移动读取或写入跨越原子操作。这就是抽象。处理器级概念,例如 16 字节比较交换操作是实现细节;如果它们可用,则应该使用它们;无论如何,实现必须提供正确的语义,它是您编程的语义,而不是实现细节。
    猜你喜欢
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 2015-03-25
    • 2023-04-09
    • 2012-07-05
    • 1970-01-01
    相关资源
    最近更新 更多