循环中哪个更耗费资源:赋值操作还是if语句?

28

考虑以下两种情况:

boolean b = false;
int i = 0;
while(i++ < 5) {
    b = true;
}

boolean b = false;
int i = 0;
while(i++ < 5) {
    if(!b) {
        b = true;
    }
}
哪种做法更“昂贵”?如果答案取决于所使用的语言/编译器,请提供相关信息。我的主要编程语言是Java。 请不要问为什么我想做其中的任何一种...它们只是指出相关问题的基本例子:在循环中变量应该重复设置相同的值,还是应该在每次循环中测试它是否保存了需要更改的值?

2
可能是Are If Thens faster then multiplication and assignment?的重复问题。 - T.E.D.
如果我记得我的编译器课程正确的话,由于分支预测器,这两个将会大致相同。我认为第一次执行分支需要更多时间,但之后编译器可以进行预测并相应地进行调整。 - Amir Raminfar
在这里,您将找到许多答案:http://www.agner.org/optimize/optimizing_cpp.pdf - ruslik
问题在于我的解决方案并不能回答他的问题,所以对我来说不是同一件事,除非你想应用一个“检查汇编”或让编译器自己处理的通用解决方案。在这种情况下,答案是相同的,我有一种感觉,这个人问的原因和我一样,计算机是如何工作的,比较需要更多的总线周期还是赋值?(或者在我的情况下,乘法需要更多的周期还是比较?) - onaclov2000
6个回答

42

请勿忘记优化俱乐部的规则

  1. 优化俱乐部的第一条规则是,不要优化。
  2. 优化俱乐部的第二条规则是,不要没有测量就进行优化。
  3. 如果您的应用程序运行速度比底层传输协议还快,那么优化结束了。
  4. 逐个因素进行优化。
  5. 没有市场营销人员,也没有市场营销时间表。
  6. 测试将持续进行。
  7. 如果这是您在优化俱乐部的首个晚上,您必须编写一个测试用例。

看来你违反了规则2。你没有任何测量。如果你真的想知道,你可以通过设置一个对比A和B场景并找到答案的测试来回答这个问题。由于不同环境之间存在许多差异,我们无法回答。


1
我并没有进行优化。我的动力是好奇心。我接受你的答案:“不同环境之间有太多差异,我们无法回答。” - heikkim
3
我在Java上进行了10万次循环测试,似乎当测试相当简单时,条件赋值略快一些。 - heikkim

5

你测试过这个代码了吗?我在一个Linux系统上,将你的第一个例子放在一个名为LoopTestNoIf.java的文件中,将你的第二个例子放在一个名为LoopTestWithIf.java的文件中,包装了一个main函数和类,并编译后,用以下bash脚本运行:

#!/bin/bash
function run_test {
  iter=0
  while [ $iter -lt 100 ]
  do
    java $1
    let iter=iter+1
  done
}
time run_test LoopTestNoIf
time run_test LoopTestWithIf

结果如下:

real    0m10.358s 
user    0m4.349s 
sys     0m1.159s 

real    0m10.339s
user    0m4.299s 
sys     0m1.178s

展示if语句在我的系统上使其稍微快一些。

我得到了类似的结果,请查看我对Andy Lester的评论。 - heikkim

1

其实,这就是我感兴趣的问题……(我希望在某个地方找到答案,避免自己测试。但事实证明,我没能找到答案……)

为了确保你(或我)的测试有效,必须进行足够多的迭代以获取足够的数据。每个迭代必须足够“长”(我指时间尺度)才能显示真正的差异。我发现即使进行10亿次迭代也不足以适应足够长的时间间隔...... 因此我写了这个测试:

for (int k = 0; k < 1000; ++k)
{
    {
        long stopwatch = System.nanoTime();

        boolean b = false;
        int i = 0, j = 0;
        while (i++ < 1000000)
            while (j++ < 1000000)
            {
                int a = i * j; // to slow down a bit
                b = true;
                a /= 2; // to slow down a bit more
            }

        long time = System.nanoTime() - stopwatch;
        System.out.println("\\tasgn\t" + time);
    }
    {
        long stopwatch = System.nanoTime();

        boolean b = false;
        int i = 0, j = 0;
        while (i++ < 1000000)
            while (j++ < 1000000)
            {
                int a = i * j; // the same thing as above
                if (!b)
                {
                    b = true;
                }
                a /= 2;
            }

        long time = System.nanoTime() - stopwatch;
        System.out.println("\\tif\t" + time);
    }
}

我在Excel中运行了三次测试,并交换了第一(“asgn”)和第二(“if”)个案例,然后再运行了三次...结果呢?四次“赢”的是“if”情况,两次“asgn”似乎是更好的情况。这表明执行可能非常敏感。但总的来说,我希望这也证明了“if”情况是更好的选择。
无论如何,谢谢...

1

你是否试图找出在总运行时间上,每次循环执行任务是否比每次循环检查并仅在满足测试条件时分配一次更快?

在上面的例子中,我猜第一个更快。你执行了5个任务分配。而在后者中,你执行了5次测试,然后再进行一次任务分配。

但是,你需要增加迭代计数并添加一些秒表计时器才能确定。


0

任何编译器(除了调试模式)都会将这两个语句进行优化处理

bool b = true;

但是一般来说,赋值和跳转的相对速度取决于处理器架构,而不是编译器。现代的超标量处理器在分支上表现很差。一个简单的微控制器每个指令使用大致相同的周期数。


0

相对于你的基本示例(以及可能是你真实的应用程序):

boolean b = false;
// .. other stuff, might change b
int i = 0;
// .. other stuff, might change i
b |= i < 5;
while(i++ < 5) {
    // .. stuff with i, possibly stuff with b, but no assignment to b
}

问题解决了吗?

但是,实际上,这将涉及到您的测试成本(通常不仅仅是if (boolean))和您的分配成本(通常不仅仅是primitive = x)。如果测试/分配成本昂贵,或者您的循环足够长,或者您有足够高的性能要求,您可能需要将其分成两个部分 - 但所有这些标准都要求您测试性能如何。当然,如果您的要求更严格(例如,b可以来回翻转),则可能需要更复杂的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接