use*_*893 15 c boolean arithmetic-expressions
我在一些论坛中遇到过这段代码:
if ( a * b * c * d == 0 ) ....
并且所有者声称这比它快一点
if (a == 0 || b == 0 || c == 0 || d == 0)
这些变量定义为:
int a, b, c, d;
并且它们的绝对值保证小于或等于100.(所以我们可以忽略溢出的可能性)
如果我们只是忽略readability并只关注性能,那么声明是否真的正确?
在我看来,第二种方法可能实际上更快,因为你有时可以利用"短路".但那么,我知道什么?!
C标准没有提到性能.是否的问题
if ( a * b * c * d == 0 )
Run Code Online (Sandbox Code Playgroud)
比...更快
if (a == 0 || b == 0 || c == 0 || d == 0)
Run Code Online (Sandbox Code Playgroud)
仅在生成在特定机器上运行的代码的特定编译器的上下文中有意义.比较它们的唯一真正方法是测量您自己的系统或您感兴趣的任何系统的性能.
尽管如此,我们仍可以猜测性能可能是什么.
正如你所说,a,b,c,和d是类型的对象int.你还说它们在[-100,+ 100]的范围内 - 但编译器并不一定知道.
编译器可以使用执行相同操作的代码替换任何表达式.
乘法是一种相对复杂的操作,并且可能比添加或比较慢.如果四个变量中的任何一个具有该值,则编译器可以识别出第一个条件为真0,并且将乘法替换为更快的任何变量.但编译器执行的每个优化都必须由编译器的开发人员明确编程,并且这种特定模式不太可能足以让它值得认识到它.
你说这些值足够小,溢出不是问题.事实上,你不能可移植性作出这样的假设; INT_MAX可以小到32767.但编译器知道int它生成代码的系统有多大.不过,除非它拥有约值信息a,b,c,并且d,它不能假设不会有溢出.
除了是,实际上,它可以做出这个假设.有符号整数溢出的行为未定义.这给了一个优化编译器权限,假设不会发生溢出(如果它发生,程序展示的任何行为无论如何都是有效的).
所以是的,编译器可以用更简单的东西替换乘法,但它不太可能这样做.
至于其他的表达,a == 0 || b == 0 || c == 0 || d == 0中,||运营商具有短路语义; 如果左操作数为真(非零),则不评估右操作数.由于CPU管道问题,这种条件代码可能会产生性能问题.由于没有任何子表达式具有副作用(假设没有声明任何变量volatile),编译器可以评估所有四个子表达式,可能是并行的,如果这更快的话.
快速实验表明,gcc -O3对于x86,不执行任何优化.对于第一个表达式,它生成执行三次乘法的代码.对于第二个,它生成条件分支,实现规范的短路评估(我不知道是否避免更快或更快).
您最好的选择是编写尽可能简单的合理代码,因为它使您的源代码更易于阅读和维护,并且因为它可能使编译器有更好的机会识别模式并执行优化.如果您尝试在源代码中进行精细的微优化,则可能会妨碍编译器的优化,因为您需要提供帮助.
不要太担心你的代码有多快,除非你测量它并发现它太慢了.如果您需要更快地编写代码,请首先关注改进的算法和数据结构.只有在失败的情况下,才考虑源级微优化.
程序优化的第一条规则:不要这样做.程序优化的第二条规则(仅限专家!):不要这样做.
- 迈克尔杰克逊
这两者并不相同.例如,在我的机器上(32位x86 MSVC),如果a,b,c和d都等于0x100那么第一个测试将通过,但第二个条件不会.
另请注意,乘法是一项代价高昂的操作,因此第一个版本不一定会更快.
编辑:为第一个版本生成的代码:
00401000 8B 44 24 04 mov eax,dword ptr [esp+4]
00401004 0F AF 44 24 08 imul eax,dword ptr [esp+8]
00401009 0F AF 44 24 0C imul eax,dword ptr [esp+0Ch]
0040100E 0F AF 44 24 10 imul eax,dword ptr [esp+10h]
00401013 85 C0 test eax,eax
00401015 75 07 jne f1+1Eh (40101Eh)
00401017 ...
Run Code Online (Sandbox Code Playgroud)
为第二个版本生成的代码:
00401020 83 7C 24 04 00 cmp dword ptr [esp+4],0
00401025 74 15 je f2+1Ch (40103Ch)
00401027 83 7C 24 08 00 cmp dword ptr [esp+8],0
0040102C 74 0E je f2+1Ch (40103Ch)
0040102E 83 7C 24 0C 00 cmp dword ptr [esp+0Ch],0
00401033 74 07 je f2+1Ch (40103Ch)
00401035 83 7C 24 10 00 cmp dword ptr [esp+10h],0
0040103A 75 07 jne f2+23h (401043h)
0040103C ...
Run Code Online (Sandbox Code Playgroud)
我的机器上的基准测试(以纳秒为单位):第一个版本在大约1.83 ns运行,第二个版本在大约1.39 ns内运行.a,b,c和d的值在每次运行期间没有变化,因此显然分支预测器可以预测100%的分支.
| 归档时间: |
|
| 查看次数: |
2863 次 |
| 最近记录: |