1

最近,我开始使用如下表达式:

res += (i + n / i) * !(n % i);

在我假设的地方,它!(n % i)的值总是 1 或 0,因此可以直接用于计算,而不是编写冗长的 if 语句,如

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 语句有更好的感觉。

谢谢,安迪

4

3 回答 3

3

是的,编译器保证按照您期望的方式运行。

不,它不会使您的代码更快,除非编译器质量非常低。编译器应该大致等效地处理这两个版本的代码,并选择它认为最好的方式来执行条件逻辑。

顺便说一句,原则上!运算符是一个条件分支。一些实现(cpu archs)可能有办法优化它而不需要真正的程序计数器分支,但相同的方法适用于大多数条件。

请注意,从优化的角度来看,您的代码可能有一种“更好”的方式。书面:

res += (i + n / i) * !(n % i);

res您已授予编译器在两个代码路径中写入的权限。在表格中:

if(!(n % i))
    res += (i + n / i);

res如果条件为真,编译器只能写入。如果res是本地的并且它的地址没有泄露,编译器可以确定无论如何执行多余的写入是安全的,但是如果地址res在函数之外是可见的,编译器必须假设其他线程可能能够访问它并且代码路径不在res抽象机中修改的代码不能在生成的代码中修改它(因为它们可能没有持有安全修改它所必需的锁)。

于 2013-02-03T20:53:21.563 回答
2

我认为“何时优化”没有一个通用的答案。我个人对此主题的看法是,在直接要求执行之前,所有优化都为时过早。哎呀,有时我会等待被多次询问(只是为了安全起见,有人真的需要它)。

除此之外,第一条语句也可以写成:

res += (n % i) ? 0 : (i + n / i);

就目前而言,乘法和否定使其难以理解。

编辑

这段代码是否保证按照我预期的方式运行

如果第一部分(与 0 或 1 相乘的部分)有副作用(比如有人在做 ++),它可能会导致细微的错误。就像我说的,只使用最自然的形式,以后担心速度。

于 2013-02-03T20:41:34.067 回答
1

运算符将!始终评估为 0 或 1,因此您的假设是正确的。

至于性能影响,任何值得称道的优化编译器都应该为if语句或三元运算符生成相同的(或等效的,性能方面的)代码。如果有疑问,请检查程序集输出(-S如果使用 gcc,则为开关)或对代码进行基准测试。

于 2013-02-03T20:52:26.813 回答