【问题标题】:Loop with a zero execution time执行时间为零的循环
【发布时间】:2015-01-02 12:00:37
【问题描述】:

是否可以有一个执行时间为零的循环?我认为即使是一个空循环也应该有一个执行时间,因为它会产生开销。

【问题讨论】:

  • 循环可能会被编译器完全展开和/或消除。
  • 优化编译器的主要工作之一是数据流分析。消除了产生不会在任何地方流动的数据的计算,包括循环。确切的行为取决于您的编译器。
  • 基于问题太宽泛而结束是没有意义的,我们对这个问题没有太多可能的答案,它非常适用于一般情况。
  • 这个问题既不适合过于宽泛的标准。正如我们从答案中凭经验看到的那样,它们的数量并不多,而且基本上只有一个精确的答案,这两个答案的不同之处仅在于它们提供的细节量。答案不会太长,虽然可以扩展,但要接近最长的最佳答案,才能完美地解决现有的主题问题。
  • @georg:但是流水线可以将某条指令的观察到的处理时间减半,因为它在同一个 CPU 周期中与另一条指令一起执行。如果“另一条指令”是您的 NOP,则只需原始指令周期即可。

标签: c++ c optimization execution-time as-if


【解决方案1】:

是的,根据 as-if 规则,编译器只有义务模拟代码的可观察行为,所以如果您有一个没有任何可观察行为的循环,则可以对其进行优化完全离开,因此实际上将有零执行时间。

示例

例如下面的代码:

int main()
{
  int j = 0 ;
  for( int i = 0; i < 10000; ++i )
  {
    ++j ;
  }
}

使用-O3 标志使用gcc 4.9 编译基本上最终会缩减为以下(see it live):

main:
  xorl  %eax, %eax  #
  ret

几乎所有允许的优化都属于as-if 规则,我知道的唯一例外是copy elison,它允许影响可观察的行为。

其他一些示例包括dead code elimination,它可以删除编译器可以证明永远不会执行的代码。例如,即使以下循环确实包含副作用,也可以对其进行优化,因为我们可以证明它永远不会被执行 (see it live):

#include <stdio.h>

int main()
{
  int j = 0 ;
  if( false ) // The loop will never execute
  {
    for( int i = 0; i < 10000; ++i )
    {
      printf( "%d\n", j ) ;
      ++j ;
    }
  }
}

循环将与前面的示例一样优化。一个更有趣的例子是循环中的计算可以推导出为一个常数,从而避免循环的需要(不确定这属于哪个优化类别),例如:

int j = 0 ;
for( int i = 0; i < 10000; ++i )
{
  ++j ;
}
printf( "%d\n", j ) ;

可以优化为 (see it live):

movl    $10000, %esi    #,
movl    $.LC0, %edi #,
xorl    %eax, %eax  #
call    printf  #

我们可以看到没有涉及循环。

标准中涵盖的as-if规则在哪里

as-if 规则在草案 C99 标准部分 5.1.2.3Program execution 中包含:

在抽象机中,所有表达式都按照 语义。一个实际的实现不需要评估一个 表达式,如果它可以推断出它的值没有被使用并且没有 产生了所需的副作用(包括任何由调用 函数或访问 volatile 对象)。

as-if rule 也适用于 C++,gcc 在 C++ 模式下也会产生相同的结果。 C++ 标准草案在1.9 部分中对此进行了介绍程序执行

本国际标准中的语义描述定义了一个 参数化的非确定性抽象机。这个国际 标准对符合的结构没有要求 实施。特别是,他们不需要复制或模仿 抽象机器的结构。相反,符合要求的实现 需要模拟(仅)抽象的可观察行为 机器如下所述。5

【讨论】:

  • 我敢打赌,如果不是所有语言规范,大多数语言规范都有 as-if 规则。如果不使用并且没有副作用,那么进行计算通常是没有意义的。只会浪费时间和精力。
  • @CraigAnderson:绝大多数情况下都是这样,但在极少数情况下您确实想要进行无用的计算,例如在加密操作中您想要所有代码路径花费相同的时间以防止timing side channel attacks
  • 换句话说:对于执行实际的、不可避免的、不可作弊的工作的真实循环,答案是:。对于只做多余事情的伪循环:Yes.
【解决方案2】:

是的 - 如果编译器确定循环是死代码(永远不会执行),那么它将不会为它生成代码。该循环的执行时间为 0,尽管严格来说它在机器代码级别不存在。

【讨论】:

    【解决方案3】:

    除了编译器优化之外,一些 CPU 架构,尤其是 DSP,具有零开销循环,因此具有固定迭代次数的循环可以被硬件有效地优化掉,参见例如http://www.dsprelated.com/showmessage/20681/1.php

    【讨论】:

      【解决方案4】:

      编译器没有义务对没有副作用且结果被丢弃的表达式或表达式的一部分进行求值。

      哈比森和斯蒂尔,C: A Reference Manual

      【讨论】:

        猜你喜欢
        • 2013-10-15
        • 1970-01-01
        • 1970-01-01
        • 2014-07-27
        • 2018-02-04
        • 1970-01-01
        • 1970-01-01
        • 2018-11-08
        • 1970-01-01
        相关资源
        最近更新 更多