如果我有一个循环来检查数组中的特定值,并且由于某种原因,必须迭代所有元素并且不能中途破坏。
以下哪项更有效:在每场比赛中盲目地设置一个标志,或者在设置之前检查标志是否为假。
bool happened = false;
while (...) {
if (...) {
happened = true;
}
}
VS
bool happened = false;
while (...) {
if (...) {
if (!happened) happened = true;
}
}
据我所知,假设内存读取与内存写入一样快(忽略第二个示例中的额外指令),两者都或多或少相等。我的结论是否正确?
答案 0 :(得分:4)
如果您使用任何有意义的优化,编译器将为您做出决定。写出最干净,最有意义的东西。对我来说这将是第一个,因为它是更少的代码,并没有引入更多的代码路径。为了好玩,我在Clang 3.4 -O3中做了一些测试:
bool happened = false;
extern volatile int dontOptMe1, dontOptMe2, dontOptMe3;
while (dontOptMe1) {
if (dontOptMe2) {
happened = true;
}
}
dontOptMe3 = happened;
VS
bool happened = false;
extern volatile int dontOptMe1, dontOptMe2, dontOptMe3;
while (dontOptMe1) {
if (dontOptMe2) {
if(!happened) happened = true;
}
}
dontOptMe3 = happened;
在伪ASM中导致以下结果:
MOV happened, 0
BRA LOOP_END
LOOP_START:
SELECTEQ dontOptMe2, 0, happened, happened, 1
LOOP_END:
BCZC dontOptMe1, LOOP_START
EXIT:
STORE dontOptMe3, happened
VS
MOV happened, 0
BCZS dontOptMe1, EXIT
LOOP:
SELECTNE dontOptMe2, 0, R2, 1, 0
SELECTEQ happened, 0, R3, 1, 0
AND R3, R2, R3
SELECTNE R3, 0, happened, 1, 0
BCZC dontOptMe1, LOOP
EXIT:
STORE dontOptMe3, happened
第一个更令人满意。这也是限制性易失性类型的一个很好的例子。我很惊讶编译器无法将第二个转换为第一个。
注意:SELECTXX表示,如果Arg1减去Arg2设置条件代码XX,则将Arg3设置为Arg4,否则将Arg3设置为Arg5。因此:SELECTNE dontOptMe2, 0, R2, 1, 0
相当于:C {/ p>中的R2 = (dontOptMe2 == 0) ? 1 : 0;
答案 1 :(得分:3)
一般来说,第一个版本更加管道友好,因为它的指令流不受跳转的干扰,因此效率更高。但这取决于特定的体系结构功能和编译器优化。
我认为这两个版本的性能差异在实际情况下并不明显。
答案 2 :(得分:1)
到目前为止,大多数答案似乎都是“取决于”,但我认为这很明显:
如果你有条件地将某个值设置为某个东西,如果它不是某个东西,那么它在逻辑上与无条件地设置该值相同。如果你很幸运,编译器会同时注意和处理两者,但如果没有,则无条件版本每次都获胜。
1:它使用较少的指令
2:额外的指令是有条件的,损害了分支预测
如果你要去
if (cond) varx = vary;
编译器使用一个条件分支(如果硬件支持,则可能是条件移动而不是分支)
如果你要去
if (cond && varx != vary) varx = vary;
编译器将简化为第一种情况,或使用两次条件跳转(或一次跳转和条件移动)。
答案 3 :(得分:0)
只有分析才能确定,但最有可能的是变量无论如何都存储在寄存器中,而不是存储在内存中(除非循环中有许多其他局部变量)并且根本没有可测量的差异。
答案 4 :(得分:0)
是的,你说的很对。它们或多或少相同。
确切地说,如果“发生”经常发生,第一种形式可能是可取的,而如果“发生”非常罕见,第二种形式可能希望是有利的。但即便如此,我认为第二种方式不会更好。
最后,它可能是一种微观优化,最好是为了提高可读性而不是性能。
答案 5 :(得分:0)
我已根据以下代码凭经验运行:
bool happened = false;
for (int i = 0; i < 1000000000; i++) {
if (i % 2) {
// uncomment the one you want to use
// happened = true;
// if (!happened) happened = true;
}
}
我每次跑了10次,平均值为2.304s,标准差为0.013s,平均值为2.399s,标准差为0.007s。双样本t检验表明happened = true;
比if (!happened) happened = true;
更快,差异具有统计学意义,p = 7e-12。