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
失败,尽管 full
和 full2
具有完全相同的数据。 (我还检查了 full
和 full2
正确对齐的位置)
所以第一次似乎CAS
可以用于64位,但第二次似乎不能。任何想法为什么?
编辑
(64位机器怎么样?)
好的,所以问题是我在我的CAS
中使用了char *
,而这些只是被检查了。所以解决方案是转换为 long long
或 uint64_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 位机器?)的主要内容,如果未能解决你的问题,请参考以下文章