[英]Do most compilers transform % 2 into bit comparison? Is it really faster?
在编程中,通常需要检查数字是奇数还是偶数。 为此,我们通常使用:
n % 2 == 0
但是,我的理解是'%'
运算符实际执行除法并返回其余数; 因此,对于上述情况,简单地检查最后一位会更快。 比方说n = 5;
5 = 00000101
为了检查数字是奇数还是偶数,我们只需要检查最后一位。 如果是1
,那么数字是奇数; 否则,它是均匀的。 在编程中,它将表达如下:
n & 1 == 0
根据我的理解,这将比% 2
更快,因为没有执行除法。 需要进行一点比较。
那我有2个问题:
1)第二种方式是否真的比第一种方式快(在所有情况下)?
2)如果1的答案是肯定的,编译器(所有语言)是否足够智能将% 2
转换为简单的比特? 或者,如果我们想要最佳性能,我们是否必须明确使用第二种方式?
是的,比特测试比整数除法快得多 , 由约10倍至20,或甚至100为128位/ 64位的64位= IDIV英特尔 。 ESP。 因为x86至少有一个test
指令,它根据按位AND的结果设置条件标志,所以你不必进行除法然后比较; 按位AND
是比较。
我决定实际检查Godbolt上的编译器输出 ,并得到一个惊喜:
事实证明,使用n % 2
作为有符号整数值(例如,从返回signed int
的函数return n % 2
)而不是仅仅测试非零( if (n % 2)
)有时会产生比return n & 1
。 这是因为(-1 % 2) == -1
,而(-1 & 1) == 1
,所以编译器不能使用按位AND。 编译器仍然避免整数除法,而是使用一些聪明的shift /和/ add / sub序列,因为它仍然比整数除法便宜。 (gcc和clang使用不同的序列。)
因此,如果您想基于n % 2
返回真值,那么您最好的选择是使用无符号类型。 这使编译器始终可以将其优化为单个AND指令。 (在godbolt上,您可以转到其他体系结构,如ARM和PowerPC,并看到unsigned even
( %
)函数和int even_bit
(按位&
)函数具有相同的asm代码。)
使用bool
(必须是0或1,而不仅仅是任何非零值)是另一种选择,但编译器必须做额外的工作才能返回(bool) (n % 4)
(或者除了n%2
之外的任何测试(bool) (n % 4)
n%2
)。 它的按位和版本将为0,1,2或3,因此编译器必须将任何非零值转换为1.(x86具有有效的setcc
指令,将寄存器设置为0或1,具体取决于在标志,所以它仍然只有2条指令,而不是1铛/ GCC使用这个,看aligned4_bool
在godbolt ASM输出)。
如果任何优化级别高于-O0
,则gcc和clang将if (n%2)
优化为我们期望的值。 另一个巨大的惊喜是icc 13 没有 。 我不明白WTF icc认为它正在对所有这些分支做 。
速度相当。
无论实现语言如何,模数版本通常都能保证整数是正数,负数还是零。 按位版本不是。
使用您认为最具可读性的内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.