关于确定的唯一方法是测试。我不得不同意,需要一个相当聪明的编译器才能产生尽可能高效的输出:
if(n%2) // or (n%2==0) and flip the order
n=n-1
else
n=n+1
可以n ^= 1;
,但我最近没有检查任何类似的东西,可以肯定地说。
至于你的第二个问题,我怀疑它有什么不同——对于这些方法中的任何一种,平等比较都会很快结束。如果你想要速度,主要要做的是避免涉及到一个分支——例如:
if (a == b)
c += d;
可以写成: c += d * (a==b);
. 查看汇编语言,第二个通常看起来有点混乱(很难将比较结果从标志获取到普通寄存器),但通过避免任何分支仍然通常表现更好。
编辑:至少我方便的编译器(gcc & MSVC )不会cmov
为. 我将代码扩展为可测试的东西。if
sete
* (a==b)
Edit2:由于 Potatoswatter 提出了另一种使用按位而不是乘法的可能性,因此我决定与其他方法一起进行测试。这是添加的代码:
#include <time.h>
#include <iostream>
#include <stdlib.h>
int addif1(int a, int b, int c, int d) {
if (a==b)
c+=d;
return c;
}
int addif2(int a, int b, int c, int d) {
return c += d * (a == b);
}
int addif3(int a, int b, int c, int d) {
return c += d & -(a == b);
}
int main() {
const int iterations = 50000;
int x = rand();
unsigned tot1 = 0;
unsigned tot2 = 0;
unsigned tot3 = 0;
clock_t start1 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot1 +=addif1(i, j, i, x);
}
clock_t stop1 = clock();
clock_t start2 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot2 +=addif2(i, j, i, x);
}
clock_t stop2 = clock();
clock_t start3 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot3 +=addif3(i, j, i, x);
}
clock_t stop3 = clock();
std::cout << "Ignore: " << tot1 << "\n";
std::cout << "Ignore: " << tot2 << "\n";
std::cout << "Ignore: " << tot3 << "\n";
std::cout << "addif1: " << stop1-start1 << "\n";
std::cout << "addif2: " << stop2-start2 << "\n";
std::cout << "addif3: " << stop3-start3 << "\n";
return 0;
}
现在真正有趣的部分是:第三版的结果非常有趣。对于 MS VC++,我们大致得到了我们大多数人可能期望的:
Ignore: 2682925904
Ignore: 2682925904
Ignore: 2682925904
addif1: 4814
addif2: 3504
addif3: 3021
使用 the&
代替*
, 会带来明显的改进——几乎和*
give over一样多if
。使用 gcc 的结果却有很大不同:
Ignore: 2680875904
Ignore: 2680875904
Ignore: 2680875904
addif1: 2901
addif2: 2886
addif3: 7675
在这种情况下,代码 usingif
的速度更接近于代码 using 的速度*
,但代码 using比&
任何一个都慢——慢得多!万一有人在乎,我发现这很令人惊讶,以至于我用不同的标志重新编译了几次,每次都重新运行了几次,依此类推,结果完全一致——使用的代码始终慢得多.&
使用 gcc 编译的第三版代码的糟糕结果让我们回到了我所说的开头 [并结束此编辑]:
正如我一开始所说的那样,“确定的唯一方法是测试”——但至少在这个有限的测试中,乘法始终优于if
. 可能存在编译器、编译器标志、CPU、数据模式、迭代计数等的某种if
组合,这有利于乘法 - 毫无疑问,差异足够小,以至于另一个方向的测试是完全可信的。尽管如此,我相信这是一种值得了解的技术。对于主流编译器和 CPU,它似乎相当有效(尽管它对 MSVC 肯定比对 gcc更有帮助)。
[edit2 的恢复:] 使用 gcc 的结果&
展示了 1)微优化可以/是编译器特定的程度,以及 2)现实生活中的结果与预期有多大不同。