C++:编译器能否优化按值传递?

Posted

技术标签:

【中文标题】C++:编译器能否优化按值传递?【英文标题】:C++: Can the Compiler Optimize a Passing by Value? 【发布时间】:2017-11-11 13:15:34 【问题描述】:

一种众所周知的编译器优化是所谓的返回值优化。这种优化基本上允许编译器不复制从函数返回的局部变量,而是移动它。

但是,我想知道如果知道函数的返回值将覆盖原始参数,是否也可以按值将参数传递给函数。

这是一个例子。假设我们有以下函数:

std::vector<Foo> modify(std::vector<Foo> data) 
    /* Do some funny things to data */
    return data;

然后按以下方式使用此函数:

std::vector<Foo> bigData = /* big data */;
bigData = modify(bigData); // Here copying the data into the function could be omitted

现在,在这种情况下,可以清楚地确定函数调用的返回值将覆盖每个值传递给函数的参数。我的问题是当前的编译器是否能够以某种方式优化此代码,以便在传递给函数时不复制参数data,或者这甚至可能是所谓的返回值优化的一部分。

更新

让我们考虑 C++11。不知下面的理解是否正确:如果按值传递给函数参数的值是r值,并且参数的类型有move-constructor,那么会使用move构造函数而不是copy构造函数。

例如:

std::vector<Foo> bigData = /* big data */;
bigData = modify(std::move(bigData));

如果假设正确,则在传递值时会消除复制操作。从已经给出的答案看来,我之前提到的优化似乎并不常见。看着这种手动方法,我真的不明白为什么,因为似乎很容易应用。

【问题讨论】:

很难确切地回答,但如果我不得不打赌:我认为你永远不会得到这种优化除非函数是内联的。 “很难明确回答”——嗯,这很简单,只需要使用您感兴趣的编译器,然后查看生成的 asm。 @UKMonkey 对于特定功能、特定编译器、特定架构、特定标志,这很容易回答,是的,我同意。这个问题更加笼统,并且没有指定函数的主体,所以我不会说它“简单”。无论如何,我尽了最大的努力来回答。 【参考方案1】:

很难确定,因为原则上编译器可以优化很多东西,只要他们确定它具有相同的行为。但是,根据我的经验,如果没有内联,这种优化就不会发生。考虑以下代码:

__attribute__((noinline)) std::vector<double> modify(std::vector<double> data) 
    std::sort(data.begin(), data.end());
    return data;


std::vector<double> blah(std::vector<double> v) 
    v = modify(v);
    return v;

您可以查看为各种编译器生成的程序集;在这里,我有带有 O3 优化的 clang 4.0:https://godbolt.org/g/xa2Dhf。如果您仔细查看程序集,您会在blah 中看到对operator new 的调用。这证明blah确实是为了调用modify而进行复制。

当然,如果发生内联,编译器删除副本应该很简单。

【讨论】:

【参考方案2】:

在 C++11 中,编译器可以确定 bigData 在函数中使用后被重新分配,并将其作为右值传递,但与 RVO(来自 c++17)不同,不能保证这一点。

对于std::vector,至少您可以通过将函数调用为modify(std::move(bigData)) 来确保发生这种情况,这将从右值引用构造modify 中的值,它无法使用RVO afaik 进行优化,因为它是函数参数,明确排除在此优化之外 (3rd point here)。但是编译器应该明白返回值是一个右值,并再次将其移动到大数据中。

某些编译器是否会忽略从对象到函数的移动以及从函数返回到对象的操作我不确定,但我不知道明确允许它,并且由于移动构造函数可能具有 observable副作用,这可能意味着它是不允许的(参见上面链接中的注释部分)。

【讨论】:

【参考方案3】:

这实际上是特定于编译器的,取决于您如何对数据执行操作(我们是否正在修改数据)。大多数情况下,除非您真正对其进行基准测试,否则您不应期望编译器会进行此类优化。我用 VS2012 编译器做了一些测试,虽然我们没有修改它,但它执行复制操作。

请看看这篇文章(Does the compiler optimize the function parameters passed by value?),希望能给你一个更好的主意。

【讨论】:

以上是关于C++:编译器能否优化按值传递?的主要内容,如果未能解决你的问题,请参考以下文章

我可以让 C++ 编译器决定是按值传递还是按引用传递?

C++:为啥对于内置(即类 C)类型,按值传递通常比按引用传递更有效

为啥要在 C++ 中按值传递对象 [重复]

是否传递指针参数,在 C++ 中按值传递?

为啥不允许将数组按值传递给 C 和 C++ 中的函数?

哪个更快?按引用传递与按值传递 C++