【问题标题】:Is there a difference in runtime between these two loops, and are there exceptions?这两个循环之间的运行时是否存在差异,是否存在异常?
【发布时间】:2013-03-05 03:44:31
【问题描述】:

考虑以下两个循环,其中 N = 10^9 或大到足以注意到效率低下的值。

Loop x = 1 to N
    total += A(x)
    total += B(x)

Loop x = 1 to N
    total += A(x)

Loop x=1 to N
    total += B(x)

每个函数取 x,执行一些任意算术计算(例如 x^2 和 3x^3 或其他什么,无关紧要),并返回一个值。

总体运行时是否会有任何差异?如果有的话,什么时候不会出现这种情况?

【问题讨论】:

  • 这两个函数是否有副作用,执行顺序可能会影响事情?如果您心中有一个具体的例子,何不试试呢?
  • 我想不出一个执行顺序会严重影响运行时的好例子——我得考虑一下。但是您的回答暗示如果顺序无关紧要(即说它们是完全独立的函数——一个计算 x^2,另一个计算 x^5,仅此而已),运行时应该是相同的(或至少可以忽略不计)除非开始一个新循环需要成本)?
  • 以上两个功能整体差异可以忽略不计。
  • @DoSparKot 我想我的问题开始被揭露为可能不正确,这取决于“任意”的含义——我想到的功能不会做任何激烈的事情, 只是一些任意操作,可能会将一些值存储在累积总和变量或其他东西中(例如,说 total += A(x) 和 total += B(x) )
  • @DoubleBass:可能在缓存访问或类似情况下会有显着差异 - 但很大程度上取决于具体情况。

标签: performance algorithm loops optimization runtime


【解决方案1】:

每个循环需要四个动作:

  1. 准备(每个循环一次)
  2. 检查停止条件(每次迭代一次)
  3. 执行循环体(每次迭代一次)
  4. 调整用于确定迭代是否应继续的值(每次迭代一次)

当您有一个循环时,您只需为第 1、2 和 4 项“支付”一次;当你有两个循环时,你为所有东西“支付”了两次。

假设调用这两个函数的顺序并不重要,那么在大多数常见情况下差异不会很明显。但是,在非常罕见的非常紧凑的循环情况下,单个循环将占用更少的 CPU 资源。事实上,loop unwinding 的一种常用技术依赖于通过多次重复主体来减少循环期间每次迭代检查和设置操作在整个 CPU 负载中的份额,并通过相应的因子减少迭代次数。

【讨论】:

    【解决方案2】:

    有几件事需要考虑。一个是您在第二个版本中为循环本身执行了两倍的指令(条件检查、递增x 等)。如果你的函数真的很微不足道,那可能是一个很大的成本。

    但是,在更实际的情况下,缓存性能、寄存器共享以及类似的事情会产生更大的影响。例如,如果两个函数都需要使用大量寄存器,您可能会发现第二个版本的性能比第一个版本差,因为编译器需要将更多的寄存器溢出到内存中,因为它每个循环都执行一次。或者如果AB 都访问相同的内存,第二个版本可能会比第二个更快,因为所有B 的访问在第二个版本中都是缓存命中,而在第一个版本中未命中。

    所有这些都高度特定于程序和平台。如果您要优化某些特定程序,则需要对其进行基准测试。

    【讨论】:

      【解决方案3】:

      主要区别在于第一个测试 X 与 N,N 次,而第二个测试 X 与 N,2N 次。

      【讨论】:

        【解决方案4】:

        循环本身有一点开销。

        在每个Iteration中你至少需要做2次操作,增加计数器,然后和结束值比较。

        所以你要多做 2*10^9 次操作。

        【讨论】:

          【解决方案5】:

          如果这两个函数都使用了大量内存,例如他们创建了一些大数组,并在每次迭代中递归修改它,则可能由于内存缓存等原因,第一个循环较慢。

          【讨论】:

            【解决方案6】:

            有很多潜在的因素需要考虑;

            1) 迭代次数——循环设置是否支配任务 2)循环比较惩罚与任务复杂度

            for (i=0;i<2;i++) a[i]=b[i];
            

            3) 函数的一般复杂度 - 有两个复杂的函数,一个可能会用完寄存器

            4) 注册依赖或本质上是任务序列 - 两个独立的任务混合与其他循环的结果取决于第一个

            5) 循环能否在预取队列上完全执行——无需缓存访问 - 混入第二个任务可能会破坏吞吐量

            6) 有什么样的缓存命中模式

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-04-29
              • 2022-07-22
              • 2016-07-16
              • 2010-09-20
              • 1970-01-01
              • 2021-05-27
              • 1970-01-01
              • 2021-08-17
              相关资源
              最近更新 更多