C++:编译器能否优化按值传递?
C++: Can the Compiler Optimize a Passing by Value?
一种众所周知的编译器优化是所谓的 return 值优化。这种优化基本上允许编译器不复制从函数 returned 的局部变量,而是移动它。
但是,我想知道如果已知函数的 return 值将覆盖原始参数,是否也可以通过值将参数传递给函数。
这是一个例子。假设我们有以下功能:
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
现在,在这种情况下,可以清楚地确定函数调用的 return 值将覆盖按值传递给函数的参数。我的问题是当前的编译器是否能够以某种方式优化此代码,以便参数 data
在传递给函数时不会被复制,或者这甚至可能是所谓的 [=34= 的一部分] 价值优化。
更新
让我们将 C++11 考虑在内。我想知道以下理解是否正确:如果按值传递给函数参数的值是右值,并且参数的类型具有移动构造函数,则将使用移动构造函数而不是复制构造函数。
例如:
std::vector<Foo> bigData = /* big data */;
bigData = modify(std::move(bigData));
如果这个假设是正确的,这就消除了传递值时的复制操作。从已经给出的答案来看,我之前提到的优化似乎并不常见。看着这个手动方法,我真的不明白为什么,因为应用起来似乎很简单。
很难确定,因为原则上编译器可以优化很多东西,只要他们确定它具有相同的行为。然而,根据我的经验,没有内联就不会发生这种优化。考虑以下代码:
__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;
}
您可以查看为各种编译器为此生成的程序集;这里我有 clang 4.0 和 O3 优化:https://godbolt.org/g/xa2Dhf。如果仔细查看程序集,您会在 blah
中看到对 operator new
的调用。这证明blah
确实是为了调用modify.
执行复制
当然,如果发生内联,编译器删除副本应该是微不足道的。
在 C++11 中,编译器可以确定 bigData
在函数中使用后被重新赋值,并将其作为右值传递,但与 RVO 不同(来自 c++ 17).
对于 std::vector
至少你可以通过将函数调用为 modify(std::move(bigData))
来确保发生这种情况,这将从它无法优化的右值引用构造 modify
中的值使用 RVO afaik,因为它是函数参数,明确排除在该优化之外 (3rd point here)。然而,编译器应该理解 return 值是一个右值,并再次将其移动到大数据中。
是否某些编译器省略了从对象到函数的移动以及从函数返回到对象的移动我不确定,但我不知道明确允许它,因为移动构造函数可以有明显的副作用,这可能意味着它是不允许的(参见上面的注释部分 link)。
这确实是特定于编译器的,取决于您如何对数据执行操作(我们是否正在修改数据)。大多数情况下,除非您真正对其进行基准测试,否则您不应该期望编译器进行此类优化。我用 VS2012 编译器做了一些测试,虽然我们没有修改它,但它执行复制操作。
请看看这个post(Does the compiler optimize the function parameters passed by value?),我希望这会给你一个更好的主意。
一种众所周知的编译器优化是所谓的 return 值优化。这种优化基本上允许编译器不复制从函数 returned 的局部变量,而是移动它。
但是,我想知道如果已知函数的 return 值将覆盖原始参数,是否也可以通过值将参数传递给函数。
这是一个例子。假设我们有以下功能:
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
现在,在这种情况下,可以清楚地确定函数调用的 return 值将覆盖按值传递给函数的参数。我的问题是当前的编译器是否能够以某种方式优化此代码,以便参数 data
在传递给函数时不会被复制,或者这甚至可能是所谓的 [=34= 的一部分] 价值优化。
更新
让我们将 C++11 考虑在内。我想知道以下理解是否正确:如果按值传递给函数参数的值是右值,并且参数的类型具有移动构造函数,则将使用移动构造函数而不是复制构造函数。
例如:
std::vector<Foo> bigData = /* big data */;
bigData = modify(std::move(bigData));
如果这个假设是正确的,这就消除了传递值时的复制操作。从已经给出的答案来看,我之前提到的优化似乎并不常见。看着这个手动方法,我真的不明白为什么,因为应用起来似乎很简单。
很难确定,因为原则上编译器可以优化很多东西,只要他们确定它具有相同的行为。然而,根据我的经验,没有内联就不会发生这种优化。考虑以下代码:
__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;
}
您可以查看为各种编译器为此生成的程序集;这里我有 clang 4.0 和 O3 优化:https://godbolt.org/g/xa2Dhf。如果仔细查看程序集,您会在 blah
中看到对 operator new
的调用。这证明blah
确实是为了调用modify.
当然,如果发生内联,编译器删除副本应该是微不足道的。
在 C++11 中,编译器可以确定 bigData
在函数中使用后被重新赋值,并将其作为右值传递,但与 RVO 不同(来自 c++ 17).
对于 std::vector
至少你可以通过将函数调用为 modify(std::move(bigData))
来确保发生这种情况,这将从它无法优化的右值引用构造 modify
中的值使用 RVO afaik,因为它是函数参数,明确排除在该优化之外 (3rd point here)。然而,编译器应该理解 return 值是一个右值,并再次将其移动到大数据中。
是否某些编译器省略了从对象到函数的移动以及从函数返回到对象的移动我不确定,但我不知道明确允许它,因为移动构造函数可以有明显的副作用,这可能意味着它是不允许的(参见上面的注释部分 link)。
这确实是特定于编译器的,取决于您如何对数据执行操作(我们是否正在修改数据)。大多数情况下,除非您真正对其进行基准测试,否则您不应该期望编译器进行此类优化。我用 VS2012 编译器做了一些测试,虽然我们没有修改它,但它执行复制操作。
请看看这个post(Does the compiler optimize the function parameters passed by value?),我希望这会给你一个更好的主意。