返回元素比通过引用发送元素并在那里修改慢吗?

问题描述 投票:0回答:3

假设我有一个函数可以生成一个大结构(在本例中是一个巨大的 std::vector),以及一个重复调用它的循环:

std::vector<int> render(int w, int h, int time){
    std::vector<int> result;
    /* heavyweight drawing procedures */
    return result;
};

while(loop){
    std::vector<int> image = render(800,600,time);
    /*send image to graphics card*/
    /*...*/
};

我的问题是:在这种情况下,GCC/Clang 是否足够聪明,可以避免在每次迭代时为巨大的 800x600x4 数组分配内存?换句话说,这段代码的执行是否类似于:

void render(int w, int h, int time, std::vector<int>& image){ /*...*/ }
std::vector<int> image;
while(loop){
    render(800,600,time,image);
    /*...*/
}

为什么问这个问题:我正在制作一个从语言到 C++ 的编译器,我必须决定走哪条路;如果我像第一个例子或最后一个例子一样编译它。第一个是微不足道的;最后一个需要一些棘手的编码,但如果它速度相当快,那么可能是值得的。

c++ optimization gcc compiler-construction clang
3个回答
1
投票

按值返回除最琐碎的对象之外的所有对象在 99% 的情况下都会变慢。 如果向量的长度是无限的,那么构建整个

std::vector<int>
的副本的工作量将是巨大的。 另外,如果你的向量最终有 1,000,000 个元素,那么这也是一个潜在的堆栈下溢的好方法。 在第一个示例中,每次通过循环都会复制构造和破坏
image
向量。 您始终可以使用 -pg 选项编译代码以打开 gprof 数据并检查结果。


1
投票

最大的问题不是内存分配,而是返回时发生的整个向量的复制。所以第二种选择要好得多。在第二个示例中,您还重复使用相同的向量,这不会为每次迭代分配内存(除非您在某个时刻执行 image.swap(smth) )。


1
投票
  • 编译器可以帮助进行复制省略,但这不是这里的主要问题。您还可以通过内联该函数来显式消除该副本(您可以阅读右值引用并移动语义以获取其他信息)
  • 编译器可能无法解决实际问题。即使一次只存在一个向量实例,在构造和销毁时,总会存在正确分配和释放该临时向量的堆内存的开销。那么它的执行方式将完全取决于标准库的底层分配器实现(std::cllocator、new、malloc()...)。分配器可以是智能的,并保留该内存以供快速重用,但也许,它不是(除了事实之外,您可以用自定义的智能分配器替换向量的分配器)。此外,它还取决于分配的内存大小、可用物理内存和操作系统。大块(相对于总内存)将更早返回。 Linux 可以进行过度提交(提供比实际可用的内存更多的内存)。但由于矢量实现或渲染器默认分别初始化(使用)所有内存,因此在这里没有用。

--> 去 2。

© www.soinside.com 2019 - 2024. All rights reserved.