64 位比较交换 (CAPS) 是不是应该在 32 位机器上工作? (或 64 位机器?)

Posted

技术标签:

【中文标题】64 位比较交换 (CAPS) 是不是应该在 32 位机器上工作? (或 64 位机器?)【英文标题】:Should 64bit Compare&Swap (CAS) work on a 32bit machine? (or 64bit machine?)64 位比较交换 (CAPS) 是否应该在 32 位机器上工作? (或 64 位机器?) 【发布时间】:2012-02-13 00:20:50 【问题描述】:

所以我读到,在 32 位机器上,可以将 CAS 操作与对齐的 64 位块一起使用。 同样,在 64 位机器中,可以使用 CAS 操作与对齐的 128 位块。

我使用的是 32 位机器,所以我尝试了以下方法:

// sizeof(long long) is 8 bytes, so 64 bits
long long y = 12;
long long z = 12;
long long x = 99;
__sync_bool_compare_and_swap(&y, z, x);

并且CAS 成功将y 的值更改为99

但后来我尝试使用 char array[8];(大小为 64 位)而不是 long long。我愿意:

char full[8] = '0', '1', '2', '3', '4', '5', '6', '7'; 
char full2[8] = '0', '1', '2', '3', '4', '5', '6', '7';   
char full3[8] = '5', '8', '9', 'G', 'X', '5', '6', 'U';
__sync_bool_compare_and_swap(full, full2, full3);  

但在这种情况下,CAS 失败,尽管 fullfull2 具有完全相同的数据。 (我还检查了 fullfull2 正确对齐的位置)

所以第一次似乎CAS可以用于64位,但第二次似乎不能。任何想法为什么?

编辑

(64位机器怎么样?)

好的,所以问题是我在我的CAS 中使用了char *,而这些只是被检查了。所以解决方案是转换为 long longuint64_t,它们是 64 位值。

但是当我需要使用 128bit 值时,我应该如何处理 64bit 机器? long long 在 64 位机器中仍然是 64 位,而 uint128_t 在 C 中似乎不存在。那么我应该转换为哪种类型? double long 在我的 64 位机器中似乎是 128 位,但是在执行以下操作时:

  double long y = 32432143243214;
  double long z = 32432143243214;

  int x = __sync_bool_compare_and_swap(&y, z, 1234321990);

我得到这个编译错误 error: incompatible type for argument 1 of ‘__sync_bool_compare_and_swap’.

【问题讨论】:

看起来你正在衰减到指针并且没有传递值 【参考方案1】:

您应该传递 full2 和 full3 的值,而不是指向它的指针。另外,您应该注意对齐方式。

__sync_bool_compare_and_swap((long long*)full,*(long long*)full2,*(long long*)full3);

(当然,这不是可移植的。如果要便携,请使用uint64_t而不是long long

【讨论】:

__sync_bool_compare_and_swap/CMPXCHG*B 本身不是可移植的。 我对其进行了测试,结果输出不正确:5, 1, 2, 3, 4, 5, 6, 7, 感谢您的回答,您能检查我的编辑(在问题中)吗? 既然您更改了答案以匹配我的答案,您会考虑添加一些原创内容还是删除它以防止噪音?为了发表评论,您发布的原始答案是 __sync_bool_compare_and_swap(full,(long long)full2,(long long)full3),没有投射第一个指针。跨度> 是的,但是如果没有我的第二条评论,用户在编辑历史之前看到的我最初的评论会令人困惑。【参考方案2】:

看来您忘记取消引用指针并进行转换了。

我测试过,这是唯一正确的组合:

__sync_bool_compare_and_swap((long long*)full, *(long long *)full2, *(long long *)full3);

您需要转换第一个参数,否则它只会交换第一个字符。

关于处理 128 位双长,这是来自 gcc 4.1.2 docs。

英特尔文档中给出的定义仅允许 使用类型 int、long、long long 以及它们的 unsigned 同行。 GCC 将允许任何整数标量或指针类型,即 1、2、4 或 长度为 8 个字节。

所以看起来你不能使用这个函数来处理这种情况。

【讨论】:

您可以将数组的名称作为地址传递 - 您不应该为此进行转换。 (不同指针类型的大小可能会有所不同,但我们这里已经用UB漏掉了) 编译器也会检查指针类型,并且这个函数对几种指针类型进行了重载,所以在这里很重要(例如 f(int*) 和 f(char*) 会根据不同的指针调用不同的函数)投掷)。我试过你的解决方案,它只交换了第一个字符。 谢谢。我没有考虑过这种超载。 感谢您的回答(再次),您能检查我的编辑(在问题中)吗?【参考方案3】:

您将char * 传递给__sync_bool_compare_and_swap。假设您的 char 数组(全部三个!)正确对齐到 64 位(如果它们以您显示的方式分配,它们可能不是 - 使用 malloc!),在传递给之前尝试转换为 (long long *) __sync_bool_compare_and_swap。否则,使用内联汇编器并直接调用CMPXCHG8B

【讨论】:

感谢您的回答,您能检查我的编辑(在问题中)吗? @FookoR.,为 128 位 CAS 问题打开一个新问题,它有一个非常不同的答案(一旦你这样做,就给我一个链接)

以上是关于64 位比较交换 (CAPS) 是不是应该在 32 位机器上工作? (或 64 位机器?)的主要内容,如果未能解决你的问题,请参考以下文章

关于windows XP 64位与32位

CPU通过总线直接与内存交换信息,这句话正确吗?

32 bit 与 64 bit 程序比较

linux系统怎样将32位系统调整成64位系统

我应该在 64 位版本中同时定义 _WIN32 和 _WIN64 吗?

想把电脑从32位改为64位?