【问题标题】:c++: Atomicity test for 128 bit compare and swapc++:128位比较和交换的原子性测试
【发布时间】:2018-08-01 21:53:54
【问题描述】:

我正在尝试验证不同英特尔 CPU 上 128 位操作的原子性,但由于某种原因,我的程序在我测试的每台机器上都未能通过原子性测试。我已经在 ivy bridge 和 Broadwell CPU 上进行了测试。我在下面包含了源代码。基本上它是同时增加两个 64 位计数器,它们组合在一起形成 128 位整数。如果原子性在 CPU 上起作用,那么低 64 位必须始终等于高 64 位。这就是这个程序正在测试的内容。但是测试总是失败,输出:

发现不一致 18550、18551

我正在运行 ubuntu 4.15,gcc 我将此代码编译为:

g++ -pthread -march=native test.cc -latomic

代码如下:

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>

#include <thread>

__int128 __attribute__((__aligned__(16))) count;

void task() {
 while(1) {
  __int128 old_count, swapped_count;
  do {
    __atomic_load(&count, &old_count, __ATOMIC_SEQ_CST);
    uint64_t old_lo = (uint64_t)old_count;
    uint64_t old_hi = ((uint64_t)(old_count >> 64));
    if (old_lo != old_hi) {
      printf("Found inconsistency %lu, %lu\n", old_lo, old_hi);
      exit(1);
    }
    __int128 __attribute__((__aligned__(16))) new_count;
    new_count = old_hi + 1;
    new_count <<= 64;
    new_count |= (old_lo + 1);
    swapped_count = __sync_val_compare_and_swap(&count, old_count, new_count);
    uint64_t new_lo = (uint64_t)swapped_count;
    uint64_t new_hi = (uint64_t)(swapped_count >> 64);
    if (new_lo != new_hi) {
      printf("Found inconsistency post swap %lu, %lu\n", new_lo, new_hi);
      exit(1);
    }
    // At this point count must have changed one way or the other
    if (count == old_count) {
      printf("Count is still the same ????\n");
      exit(1);
    }
  }  while (old_count != swapped_count);
 }  // while (1)
}

int main() {
  count = 0;
  for (int i = 0; i < 2; i++)
    new std::thread(task);

  while(1)
    sleep(1);
}

【问题讨论】:

  • 你看到了什么?它是如何失败的?
  • 我点击了那个 printf,它说“发现不一致”,即高 64 位与低 64 位不同。这表明这 128 位不是原子更新的。
  • on different intel CPUs 哪些 CPU?
  • Kamil,我确实提到了 Ivy 桥和 Broadwell。另外,我不认为它是另一个帖子的重复。

标签: c++ gcc g++ atomic


【解决方案1】:

所以我发现 gcc 没有以无锁方式实现 16Byte 原子。在用 gdb 调试后,我发现 gcc 中的 __atomic*_16() 函数使用了锁。另一方面 __sync*_16() 函数使用lock cmpxchg16 指令。所以马克的建议是对的。您不能将 __sync 与 __atomic 混合使用,并且通常 __atomic(特别是对于 16 字节操作)不是无锁的(还没有?)。

所以我最终实现了自己的 16 字节原子加载和 CAS 原语。我在下面粘贴该代码。使用这些原语,上面的代码就可以工作了。

union alignas(16) atomic_u128 {
  unsigned __int128 val;
  struct {
    volatile uint64_t lo, hi;
  };
};

// Atomically read a 128 bit unsigned.
__attribute__((always_inline)) inline unsigned __int128 AtomicLoad128(
    register atomic_u128 *src) {
  atomic_u128 ret;
  __asm__ __volatile__ (
      "xor %%ecx, %%ecx\n"
      "xor %%eax, %%eax\n"
      "xor %%edx, %%edx\n"
      "xor %%ebx, %%ebx\n"
      "lock cmpxchg16b %2"
      : "=&a"(ret.lo), "=d"(ret.hi)
      : "m"(*src)
      : "cc", "rbx", "rcx" );
  return ret.val;
}

__attribute__((always_inline)) inline bool AtomicCAS128(
    volatile atomic_u128 *src, atomic_u128 *expected,
    atomic_u128 desired) {
  bool result;
  atomic_u128 e;
  e.val = expected->val;
  __asm__ __volatile__ (
    "lock cmpxchg16b %1"
    : "=@ccz" ( result ), "+m" ( *src ), "+a"(e.lo), "+d"(e.hi)
    : "c" ( desired.hi ), "b" ( desired.lo )
    : "cc");
  if (!result)
    expected->val = e.val;

  return result;
}

【讨论】:

    猜你喜欢
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 2023-03-24
    • 2010-12-03
    • 2011-04-09
    • 2011-10-29
    相关资源
    最近更新 更多