【问题标题】:can the compiler inline methods that generate objects within a loop?编译器可以内联在循环中生成对象的方法吗?
【发布时间】:2012-06-22 23:18:20
【问题描述】:

只是出于我自己的好奇心而提出的问题。我多次听说在编写方法时最好使用复制/销毁范式。所以如果你有这样的方法:

OtherClass MyClass::getObject(){
    OtherClass returnedObject;
    return returnedObject;
}

假设编译器将通过内联方法并在调用getObject的方法的堆栈上生成类来优化这一点。我想知道在这样的循环中如何工作

for(int i=0; i<10; i++){
    list.push_back(myClass.getObject());
}

编译器是否会将 10 个 OtherClass 实例放在堆栈上,以便可以内联此方法并避免在未优化的代码中发生的复制和破坏?像这样的代码呢:

while(!isDone){
     list.push_back(myClass.getObject());
    //other logic which decides rather or not to set isDone
}

在这种情况下,编译器不可能知道getObject 将被调用多少次,因此可以推测它可以将任何内容预分配到堆栈中,所以我的假设是没有进行内联并且每次调用该方法时我将支付复制OtherObject的全部费用?

我意识到所有编译器都是不同的,这取决于编译器是否认为该代码是最佳的。我只是笼统地说,大多数编译最有可能如何响应?我很好奇这种优化是如何完成的。

【问题讨论】:

  • 将内联视为代码复制+粘贴。如果可以复制/粘贴,则可以内联。编译器是否会在特定情况下这样做是另一回事。
  • otherClass returnedObject(); 构造什么都不做 - 不会创建任何对象
  • @sharptooth - 已编辑以删除错误,因为这对他的问题不是必需的。
  • 我认为您有两个潜在的优化需要查看:内联和循环展开。您还可以查看 c++11 中的 std::vector::emplace_back() 。最后,尝试制作一个示例并在各种优化级别对其进行编译。

标签: c++ optimization compiler-construction


【解决方案1】:
for(int i=0; i<10; i++){
    list.push_back(myClass.getObject());
}

编译器是否会将 10 个 OtherClass 的实例放在堆栈上,以便可以内联此方法并避免在未优化的代码中发生的复制和销毁?

需要将 10 个实例放在堆栈上只是为了避免复制和销毁......如果有空间可以返回一个对象,无论是否返回值优化,它可以重复使用该空间 10 次 - 每次从相同的堆栈空间复制到列表 push_back 的一些新的堆分配内存。

分配新内存并安排 myClass.getObject() 直接在该内存中构造对象也是编译器的权利。

此外,如果优化器选择展开循环,它可能会调用 myClass.getObject() 10 次 - 即使有一些重叠或并行性 - 如果它可以以某种方式说服自己产生相同的整体结果。在这种情况下,它确实需要为 10 个返回对象提供空间,这取决于编译器是在堆栈上还是通过一些神奇的优化优化,直接在堆内存中。

在实践中,我希望编译器需要从堆栈复制到堆 - 我非常怀疑任何主流编译器足够聪明,可以在堆内存中安排直接构造。循环展开和 RVO 是常见的优化。但是,即使两者都起作用,我希望每次调用 getObject 都会在堆栈上串行构造一个结果,然后将其复制到堆中。

如果您想“知道”,请编写一些代码来测试您自己的编译器。您可以让构造函数写出“this”指针值。

这样的代码怎么样:

while(!isDone){
     list.push_back(myClass.getObject());
    //other logic which decides rather or not to set isDone
}

代码越复杂,越不习惯,编译器编写者就越不可能为它进行优化。在这里,您甚至没有向我们展示我们可以推测的复杂程度。试试你的编译器和优化设置,看看....

【讨论】:

    【解决方案2】:

    这取决于哪个编译器在哪个操作系统上的哪个版本。

    为什么不让你的编译器输出它的程序集,你可以自己看看。

    gcc - http://www.delorie.com/djgpp/v2faq/faq8_20.html

    视觉工作室 - Viewing Assembly level code from Visual C++ project

    【讨论】:

      【解决方案3】:

      一般来说,优化编译器可以对您的代码进行任何更改,只要生成的程序的行为没有明显改变。这包括inlineing 函数(或不包括),即使该函数没有被程序员标记为inline

      【讨论】:

        【解决方案4】:

        编译器唯一需要关心的是程序的行为。如果优化保持程序逻辑和数据完整,则优化是合法。输入(所有可能的程序输入)必须以与没有优化相同的方式输出(所有可能的程序输出)。

        这种特定的优化是否可能(当然是,是否是实际的优化是另一回事!)取决于目标平台指令集以及是否可行。 p>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-04-04
          • 1970-01-01
          • 1970-01-01
          • 2017-11-05
          • 1970-01-01
          • 1970-01-01
          • 2017-02-05
          • 2011-06-19
          相关资源
          最近更新 更多