回到我们使用简单的单周期或多周期 CPU 的时代,使用这种技巧来避免代价高昂的内存解引用或将寄存器溢出到堆栈中会更加便宜。然而,我们现在有了带有大量管道的 CPU。P4的管道在其管道中有20到31个(大约)阶段,在这些阶段中,对寄存器的读取和写入之间的任何依赖都可能导致整个事件停止。Xor 交换在 A 和 B 之间有一些非常严重的依赖关系,这些依赖关系实际上根本不重要,只会在实际中阻碍管道的运行。停滞的管道会导致代码路径变慢,如果这个交换位置在内部循环中,那么移动将会非常缓慢。
// A,B = 1,2
A = A+B // 3,2
B = A-B // 3,1
A = A-B // 2,1
顺便说一下,在单个“指针”中编码双向链表有一个老技巧。
假设您有一个位于地址 A、 B 和 C 的内存块列表。每个块中的第一个单词分别是:
// first word of each block is sum of addresses of prior and next block
0 + &B // first word of block A
&A + &C // first word of block B
&B + 0 // first word of block C
如果你可以访问块 A,它会给你 B 的地址,要访问 C,你可以取 B 中的“指针”,然后减去 A,依此类推。反过来也一样。要沿着列表运行,您需要保留指向两个连续块的指针。当然,您可以使用 XOR 代替加法/减法,因此不必担心溢出。
a = a XOR b
b = a XOR b = (a XOR b) XOR b
a = a XOR b = (a XOR b) XOR (a XOR b) XOR b
交换意味着“ a XOR b”等于“ b XOR a”:
a = a XOR b
b = a XOR b = (a XOR b) XOR b
a = a XOR b = (a XOR b) XOR (a XOR b) XOR b
= (b XOR a) XOR (a XOR b) XOR b
结合性意味着“(a XOR b) XOR c”等于“ a XOR (b XOR c)”:
a = a XOR b
b = a XOR b = (a XOR b) XOR b
= a XOR (b XOR b)
a = a XOR b = (a XOR b) XOR (a XOR b) XOR b
= (b XOR a) XOR (a XOR b) XOR b
= b XOR (a XOR a) XOR (b XOR b)
XOR 中的逆元素就是它自己,这意味着任何与它相关的值都是零:
a = a XOR b
b = a XOR b = (a XOR b) XOR b
= a XOR (b XOR b)
= a XOR 0
a = a XOR b = (a XOR b) XOR (a XOR b) XOR b
= (b XOR a) XOR (a XOR b) XOR b
= b XOR (a XOR a) XOR (b XOR b)
= b XOR 0 XOR 0
XOR 中的单位元素是零,这意味着任何值为零的 XOR 都保持不变:
a = a XOR b
b = a XOR b = (a XOR b) XOR b
= a XOR (b XOR b)
= a XOR 0
= a
a = a XOR b = (a XOR b) XOR (a XOR b) XOR b
= (b XOR a) XOR (a XOR b) XOR b
= b XOR (a XOR a) XOR (b XOR b)
= b XOR 0 XOR 0
= b XOR 0
= b
A = 1100
B = 0101
A ^= B; => A = 1100 XOR 0101
(A = 1001)
B ^= A; => B = 0101 XOR 1001
(B = 1100)
A ^= B; => A = 1001 XOR 1100
(A = 0101)
A = 0101
B = 1100