x86 asm函数:14个字节的机器代码
uint64_t版本:24个字节
x86-64 SysV调用约定(x
在中edi
),但是相同的机器代码也将在32位模式下工作。(lea
将将解码为lea eax, [edi + eax*2]
,结果相同)。
0000000000000040 <onemask_even>:
40: 89 f8 mov eax,edi
42: 25 55 55 55 55 and eax,0x55555555
47: 29 c7 sub edi,eax
49: d1 ef shr edi,1
4b: 8d 04 47 lea eax,[rdi+rax*2]
4e: c3 ret
4f: <end>
0x4f - 0x40
= 14个字节
这是使用xnor极好的一次掩膜思想的编译器输出的相反方法。(和相反的术语:低位是位0,它是偶数,不是奇数。)
unsigned onemask_even(unsigned x) {
unsigned emask = ~0U/3;
unsigned e = (x & emask);
return e*2 + ((x - e) >> 1);
}
我发现编译器没有任何改进。我可能将其写为mov eax, 0x555...
/ and eax, edi
,但是长度相同。
对于64位整数,相同的功能占用24个字节(请参见godbolt链接)。我看不到任何比10字节短的方式movabs rax, 0x55...
来在寄存器中生成掩码。(x86的div
指令比较笨拙,因此将全数除以3的无符号除法将无济于事。)
我确实提出了一个循环来在rax中生成掩码,但是它是10个字节(与的长度完全相同mov imm64
)。
# since 0x55 has its low bit set, shifting it out the top of RAX will set CF
0000000000000000 <swap_bitpairs64>:
0: 31 c0 xor eax,eax ; old garbage in rax could end the loop early
0000000000000002 <swap_bitpairs64.loop>:
2: 48 c1 e0 08 shl rax,0x8
6: b0 55 mov al,0x55 ; set the low byte
8: 73 f8 jnc 2 <swap_bitpairs64.loop> ; loop until CF is set
000000000000000a <swap_bitpairs64.rest_of_function_as_normal>:
# 10 bytes, same as mov rax, 0x5555555555555555
# rax = 0x5555...
a: 48 21 f8 and rax,rdi
...
如果我们知道其中的现有字节均未rax
设置低位,则可以跳过xor
,这将是8个字节长。
该答案的先前版本使用loop
insn 进行了10个字节的循环,但是0xFFFFFFFFFFFFFF08
由于我只设置了,因此迭代的运行时间最差cl
。