最近,我开始使用如下表达式:
res += (i + n / i) * !(n % i);
我假设,!(n % i)
的值始终为1或0,因此可以直接用于计算,而不是像
if(!(n % i))
res += (i + n / i);
如果您想知道,这些行是从我编写的函数中获取的,用于计算数字n
的正确除数之和:
unsigned int sum_of_divisors(unsigned int n)
{
unsigned int res = 1;
unsigned int i;
for(i = 2; i < sqrt(n); ++i)
res += (i + n / i) * !(n % i);
res += i * (i * i == n);
return res;
}
我的问题是,此代码是否保证按照我的预期行为进行操作?这对此有何影响(乘法与条件跳转)?如果合适,编译器会这样做吗?
编辑:请注意,我并不特别关注实际代码的性能。我只是想知道,出于纯粹的专业兴趣,两者中哪一个会表现得更好,为什么,以及编译器如何处理每个案例。
至于我这样写的原因,它适用于我的大脑:)
很难描述,但我有一个更好的感觉,乘以1或0而不是三元运算符或if语句,至少在某些情况下。
谢谢, 安迪
答案 0 :(得分:3)
是的,保证编译器的行为符合您的预期。
不,它不会使你的代码更快,除非编译器真的是低质量的。编译器应该大致相等地处理代码的两个版本,并选择它认为最适合执行条件逻辑的方式。
顺便说一下,原则上,!
运算符是一个条件分支。一些实现(cpu archs)可能有办法优化它而不需要真正的程序计数器分支,但相同的方法适用于大多数条件。
请注意,从优化的角度来看,您的代码可能只有单向“更好”。写作:
res += (i + n / i) * !(n % i);
您已授予编译器在两个代码路径中写入res
的权限。形式如下:
if(!(n % i))
res += (i + n / i);
如果条件为真,编译器只能写入res
。如果res
是本地的并且其地址没有泄露,编译器可以确定无论如何都可以安全执行多余的写入,但是如果res
的地址在函数外部可见,则编译器必须假设其他线程可能能够访问它,并且在抽象机器中不修改res
的代码路径不得在生成的代码中修改它(因为它们可能没有锁定安全地修改它所需的锁)。
答案 1 :(得分:2)
我认为“何时优化”并不是一般性的答案。我个人对这个主题的看法是,所有优化都是不成熟的,直到被直接要求执行它。有时我会等待被多次询问(只是为了安全起见某人真的需要它)。
除此之外,第一个陈述也可以写成:
res += (n % i) ? 0 : (i + n / i);
因为它是乘法而且否定使它难以理解。
此代码保证按照我希望的方式运行
如果第一部分(你用0或1乘以)有副作用(就像有人做++),它可能会导致细微的错误。就像我说的那样,只需使用最自然的形式,然后再担心速度。
答案 2 :(得分:1)
!
运算符将始终求值为0或1,因此您的假设是正确的。
对于性能影响,任何有价值的优化编译器都应该为if
语句或三元运算符生成相同的(或等效的,性能方面的)代码。如果有疑问,请检查装配输出(-S
开关,如果使用gcc)或基准代码。