【问题标题】:Which is faster? Comparison or assignment?哪个更快?比较还是赋值?
【发布时间】:2009-05-26 12:28:41
【问题描述】:

我正在做一些编码,我必须编写这种代码:

if( array[i]==false )
    array[i]=true;

不知道是不是应该改写成

array[i]=true;

这就提出了一个问题:比较比分配快吗?

语言之间的差异如何? (例如 java 和 cpp 的对比)

注意:我听说“过早的优化是万恶之源”。我认为这不适用于这里:)

【问题讨论】:

  • 除非您正在为处理器非常慢的嵌入式系统编写程序,否则您不必担心。即使在那个时候你也不愿意手动优化
  • array[i] 可以是 bool 以外的任何东西吗?如果不是,那么重写是正确的。
  • 是的,该数组是一个布尔数组。
  • 对于所有发布“意见”的人,关于什么更快以及为什么更快,请停止。了解给定处理器上给定编译器更快的唯一方法是对其进行基准测试。意见不计入可以衡量和测试的地方。对提问者来说,这不是什么可担心的事情,但如果你必须担心,那么测试它超过 1,000,000 次迭代,获得几次运行的平均时间,改变它,看看有什么不同。从理论上讲,一个可能比另一个更快,但对于原始数据类型,它们实际上是相同的。
  • @Binary Worrier:你怎么知道 1000000 次迭代是数组的正确大小?对于现代处理器,避免缓存未命中是重要的部分,而不是计算指令周期。因此,基准测试应该以实际的数据大小运行。

标签: performance optimization refactoring


【解决方案1】:

这不仅仅是premature optimization,这是micro-optimization,这是一个无关紧要的干扰。

假设您的数组是布尔类型,那么您的比较是不必要的,这是唯一相关的观察结果。

【讨论】:

  • 我同意..从可读性的角度来看可能会很好,但如果进行更改以“改善”性能,则肯定不会。
  • @cletus,如果只是一次调用,那就是微优化。如果是 无数次 调用,那就是宏观优化。无论如何,stackoverflow.com/q/23228359/632951 的相关线程
  • 我测试了布尔赋值和比较。结果表明,虽然我同意你的逻辑,但比较更快。在 .Net 4.6.1 中测试
【解决方案2】:

好吧,既然你说你确定这很重要,你应该只编写一个测试程序并测量以找出差异。

如果对分配在内存中分散地址的多个变量执行此代码,则比较会更快。通过比较,您只会将数据从内存读取到处理器缓存,如果您在缓存决定刷新该行时不更改变量值,它将看到该行没有更改并且无需将其写回到内存。这可以加快执行速度。

【讨论】:

  • 同意,另一方面,一些架构也有分支的惩罚......只有通过测量才能找到真正的答案。或者查看生成的汇编代码,看看它是否发生了变化(我的猜测是没有)。
  • 缓存不会自动执行此操作吗?即检查写入的数据是否与那里的数据相同。
  • @julesjacobs:不确定它是否检查是否相等。它肯定会检查它是否已更新,但我从未听说过它会检查更新是否为实际更改。请牢记这一点。
【解决方案3】:

编辑:我用 PHP 编写了一个脚本。我只是注意到其中有一个明显的错误,这意味着最佳情况下的运行时间计算不正确(可怕的是没有其他人注意到!)

最好的情况胜过直接赋值,但最坏的情况比普通赋值差很多。就实际数据而言,分配可能是最快的。

输出:

  • 0.0119960308075 秒内完成分配
  • 0.0188510417938 秒内的最坏情况比较
  • 0.0116770267487 秒内的最佳情况比较

代码:

<?php
$arr = array();

$mtime = explode(" ", microtime());
$starttime = $mtime[1] + $mtime[0];

reset_arr($arr);

for ($i=0;$i<10000;$i++)
    $arr[i] = true;


$mtime = explode(" ", microtime());
$firsttime = $mtime[1] + $mtime[0];
$totaltime = ($firsttime - $starttime);
echo "assignment in ".$totaltime." seconds<br />"; 

reset_arr($arr);

for ($i=0;$i<10000;$i++)
    if ($arr[i])
        $arr[i] = true;

$mtime = explode(" ", microtime());
$secondtime = $mtime[1] + $mtime[0];
$totaltime = ($secondtime - $firsttime);
echo "worst case comparison in ".$totaltime." seconds<br />"; 

reset_arr($arr);

for ($i=0;$i<10000;$i++)
    if (!$arr[i])
        $arr[i] = false;

$mtime = explode(" ", microtime());
$thirdtime = $mtime[1] + $mtime[0];
$totaltime = ($thirdtime - $secondtime);
echo "best case comparison in ".$totaltime." seconds<br />"; 

function reset_arr($arr) {
    for ($i=0;$i<10000;$i++)
        $arr[$i] = false;
}

【讨论】:

  • 这很有趣:分配实际上更快!
  • 广告也有很大的差距:14 毫秒与 22 到 39 毫秒。这大约是 2 倍!
  • 是的,是的!非常感谢您终于检查了这一点。每次我遇到这样的情况,我都会精神崩溃,因为我永远不知道该选择什么。我的逻辑是:每次运行代码时,赋值只是设置。但是比较,是阅读,比较,然后在必要时进行设置。所以感谢上帝,我总是去分配,这真的有助于我的强迫症 xD
【解决方案4】:

我相信如果比较和赋值语句都是原子的(即一个处理器指令)并且循环执行 n 次,那么在最坏的情况下比较然后分配将需要 n+1(在每次迭代中比较加上设置分配)执行,而不断分配布尔值将需要 n 次执行。因此,第二个效率更高。

【讨论】:

    【解决方案5】:

    取决于语言。然而,遍历数组也可能代价高昂。如果数组在连续内存中,最快的方法是使用 memcpy 在整个数组中写入 1 位(255 秒),假设您的语言/编译器可以做到这一点。

    因此总共执行0读-1写,没有读/写循环变量/数组变量(每个循环2读/2写)数百次。

    【讨论】:

      【解决方案6】:

      我真的不希望这样微不足道的事情会有任何明显的性能差异,因此可以肯定的是,它可以为您提供清晰、更具可读性的代码。我认为这将始终分配为 true。

      【讨论】:

      • 我也不期望性能差异。从性能 POV 中了解更好的选择是一个问题。
      【解决方案7】:

      不妨试试看:

      if(!array[i])
          array[i]=true;
      

      但真正确定的唯一方法是分析,我敢肯定几乎任何编译器都会认为与 false 的比较是不必要的并对其进行优化。

      【讨论】:

      • 如果你的编译器是好的,这将产生相同的机器代码。
      【解决方案8】:

      这完全取决于数据类型。分配布尔值比首先比较它们要快。但对于较大的基于值的数据类型,这可能并非如此。

      【讨论】:

        【解决方案9】:

        正如其他人所指出的,这是微优化。

        (在政治或新闻业中,这被称为肚脐凝视 ;-)

        程序是否足够大,可以进行多层函数/方法/子例程调用?

        如果是这样,它可能有一些可以避免的调用,而这些调用可能会浪费数百个与低级低效率一样多的时间。

        假设你有removed those(很少有人这样做),那么一定要在秒表下运行它 10^9 次,看看哪个更快。

        【讨论】:

          【解决方案10】:

          你为什么要写第一个版本?在设置为真之前检查是否为假有什么好处。如果您总是将其设置为 true,那么请始终将其设置为 true。

          当您遇到性能瓶颈并追溯到不必要地设置单个布尔值时,请回来与我们联系。

          【讨论】:

            【解决方案11】:

            我记得在一本关于汇编语言的书中,作者声称如果可能的话,应该避免 if 条件。 如果条件为假并且执行必须跳转到另一行,则速度要慢得多,从而大大降低了性能。此外,由于程序是用机器代码执行的,我认为“if”在每种(编译的)语言中都比较慢,除非它的条件几乎一直都是真的。

            【讨论】:

            • 这种方法被称为非分支代码,它是高度优化代码的可行方法,或者当一致和可靠的时序很重要时,嵌入式系统中经常出现这种情况。跨度>
            【解决方案12】:

            如果你只是想翻转值,那么做:

            array[i] = !array[i];
            

            虽然使用它的性能实际上更差,因为它不必只检查一次真假值然后设置,而是检查两次。

            如果您声明一个 1000000 元素数组的真、假、真、假模式比较会更慢。 (var b = !b) 本质上是检查两次而不是一次

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-05-16
              • 2011-03-26
              • 2012-06-12
              • 2010-09-29
              • 2011-03-14
              • 2012-07-15
              • 2011-09-22
              • 2018-10-31
              相关资源
              最近更新 更多