uint64_t n; // two 32-bit integers
return ( (uint32_t)(n >> 32) == (uint32_t)n );
将32个最高有效位与uint64_t的32个最低有效位进行原子比较的最快方法是什么?
我认为一个可怕的解决方案是:获取自旋锁,读取32 LSB,读取32 MSB,比较得到结果,释放自旋锁,返回结果。有没有办法在不必使用自旋锁的情况下做到这一点?
答案 0 :(得分:9)
如何在两个不同的地址上使用比较交换操作?
类似于:CMPXCHG (int*)&n, (((int*)&n)+1)
(注意 - 这实际上不起作用)。
编辑:将语法更改为更接近实际的x86语法。
如Serge所指出的那样编辑2:,大多数程序集都不支持在汇编指令中使用两个内存地址,因此这种方式无法直接从内存中运行。这意味着该方法不能用于以原子方式比较64位变量的两个32位部分。
某些程序集(至少是PowerPC)能够提供特殊指令(对于PowerPC,LWARX和STWCX),可以使其以多线程安全的方式工作,但它不是OP所要求的,也不是为x86工作。
答案 1 :(得分:7)
这整个操作(内存中两个值的原子比较)是没有意义的,除非你还能确保写它们总是原子的。它也受到固有的竞争条件的影响;当你确定他们是平等的时候他们可能已经改变了,反之亦然。无论你想要解决的问题几乎肯定都需要锁定,而不是原子操作。
答案 2 :(得分:3)
只有在平台上能够以原子方式检索64位数时,才能无锁地执行此操作。如果有可能那么首先 - 你以自己喜欢的方式原子地检索64位值(例如64位窗口上的InterlockedOr64(ptr,0),如果你有32位x86 CPU就没有办法 - 除非你英特尔CPU不比奔腾更老,你确保你的64位值是64位对齐,不确定其他供应商的x86 CPU),第二 - 与你检索的值进行比较。
你显然不能以便携方式做到这一点。在无法自动获取64位数字的平台上,没有锁定就无法做到这一点。
修改强>
由于一些严重误导性的想法在本次讨论中受到了极大的欢迎,我觉得我有责任写一些关于未能使用比较和交换32位数字来解决问题的笔记。
假设我们有x86平台,那么我们可以编写asm代码:
mov eax, [num+4]
lock cmpxchg [num], eax
jz equal_case_code
; non-equal case code follows
equal_case_code:
; equal case code follows
显然这个实现不是原子的 - 在mov
和cmpxchg
指令之间可能会中断线程(因为在一条指令中不允许有两个内存操作数)。
来自不同API的32位比较和交换功能(如来自Win32 API的InterlockedCompareExchange)无法提供正确的解决方案,因为它们的语义只允许原子访问一个32位内存地址。
答案 3 :(得分:0)
使用联盟怎么样?像这样:
typedef union {
uint32_t small[2];
uint64_t full;
} bigint_t;
然后你去做:
uint64_t n; // two 32-bit integers
bigint_t mybigint;
mybigint.full = n;
return mybigint.small[0] == mybigint.small[1];
我不知道这是否是最快的,但是如果你没有将uint64_t复制到union中但直接使用union,它应该非常快,因为它不需要做任何比较。
答案 4 :(得分:0)
使用内联汇编将64位整数加载到MMX或SSE寄存器(64位读取是原子的),然后比较这两半。