C++11中循环中函数返回值的内存分配:如何进行优化?

3

我想进行一些过早的优化,并想知道以下内容。

如果有一个for循环,循环内部调用一个返回容器(如vector)的函数,并将其作为rvalue使用move语义捕获到循环中的变量中,例如:

std::vector<any_type> function(int i)
{
  std::vector<any_type> output(3);
  output[0] = i;
  output[1] = i*2;
  output[2] = i-3;
  return(output);
}

int main()
{
  for (int i = 0; i < 10; ++i)
  {
    // stuff
    auto value = function(i);
    // do stuff with value ...
    // ... but in such a way that it can be discarded in the next iteration
  }
}

如果应用移动语义(且函数不会被内联),编译器在内存方面如何处理?我想最有效的做法是为所有值分配单个内存块,包括函数内部和for循环外部的值,在每次迭代中都会被覆盖。
我主要关心这个问题,因为在我的实际应用程序中,我创建的向量比这里给出的示例要大得多。我担心如果我使用这样的函数,分配和销毁过程将占用大量无用的时间,因为我已经知道我将多次使用该固定数量的内存。所以,我实际上正在询问是否有一些方式可以优化编译器以实现以下形式:
void function(int i, std::vector<any_type> &output)
{
  // fill output
}

int main()
{
  std::vector<any_type> dummy; // allocate memory only once
  for (int i = 0; i < 10; ++i)
  {
    // stuff
    function(i, dummy);
    // do stuff with dummy
  }
}

我特别关注的是GCC实现,但也想知道例如Intel编译器的做法。


使用 GCC,您可以使用 -S 选项告诉它生成汇编代码而不是目标/可执行文件,然后检查生成的汇编代码以了解其功能。 - Some programmer dude
嗯,我以前从没看过汇编代码,但我会试一下,谢谢。 - egpbos
2个回答

5

在这里,最可预测的优化是RVO。当一个函数返回一个对象时,如果它被用来初始化一个新变量,编译器可以省略额外的拷贝和移动,直接在目标上构造(这意味着程序可以包含两个版本的函数,具体取决于使用情况)。

在这里,您仍然需要支付在每次循环迭代中分配和销毁向量内部缓冲区的费用。如果这是不可接受的,您将不得不依靠其他解决方案,例如std::array,因为您的函数似乎使用固定大小的维度,或者在循环之前移动向量并重用它。


2
我认为最有效的做法是为所有值分配一个单独的内存块,无论是在函数内部还是在for循环外部,这些值将在每次迭代中被覆盖。
我不认为任何现有的编译器可以做到这一点。如果你想获取更多见解,请观看Chandler Carruth的讲座
如果你需要这种优化,你需要自己实现:在循环外部分配向量,并通过非const引用作为参数传递给function()。当你完成后别忘了调用clear()或首先在function()内部调用clear()
所有这些与移动语义无关,在这方面C++11没有改变。
如果您的循环是繁忙的循环,那么在每次迭代中分配一个容器可能会让您付出很高的代价。您可能比预期更容易陷入这种情况。Andrei Alexandrescu在他的演讲Writing Quick Code in C++, Quickly中提出了一个例子。令人惊讶的是,在像他的例子中这样一个紧密循环中进行不必要的堆分配可能比实际的文件IO更慢。我很惊讶地看到了这一点。顺便说一下,容器是std::string

我在阅读您的回答之前稍微修改了我的问题。实际上,我可以像第二个代码示例中所建议的那样自己解决它。如果编译器确实无法完成此操作,那就没办法了。感谢您的回答! - egpbos
1
@egpbos 是的,很抱歉,目前的编译器基本上都不会那么聪明地进行这种转换。在StackOverflow这里,我们不说谢谢,而是通过点赞和接受答案来表达感谢。 ;) - Ali
我会再等一会儿再接受你的答案。也许会有人过来为我编写一个新的编译器,以便给我想要的东西:P - egpbos
@egpbos,我只能推荐我在答案中提供的Chandler的演讲。它会让你对当前编译器的能力感到幻灭。:(并不是说不可能做到这一点;只是在实践中实现它太复杂了。是的,令人失望。 - Ali

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