Mic*_*zyk 26 c++ compiler-optimization rvalue-reference visual-c++ c++11
有时将复杂或长表达式分成多个步骤是明智的(例如,第二个版本不是更清楚,但它只是一个例子):
return object1(object2(object3(x)));
Run Code Online (Sandbox Code Playgroud)
可以写成:
object3 a(x);
object2 b(a);
object1 c(b);
return c;
Run Code Online (Sandbox Code Playgroud)
假设所有3个类都实现了以rvalue作为参数的构造函数,第一个版本可能更快,因为临时对象被传递并可以移动.我假设在第二个版本中,局部变量被认为是左值.但是如果以后没有使用变量,那么C++ 11编译器是否会优化代码,因此变量被认为是rvalues,两个版本的工作方式完全相同?我最感兴趣的是Visual Studio 2013的C++编译器,但我也很高兴知道GCC编译器在这个问题上的行为.
谢谢,米哈尔
jua*_*nza 24
在这种情况下,编译器不能破坏"as-if"规则.但是你可以std::move用来达到预期的效果:
object3 a(x);
object2 b(std::move(a));
object1 c(std::move(b));
return c;
Run Code Online (Sandbox Code Playgroud)
Mat*_* M. 14
正如juanchopanza所说,编译器不能(在C++级别)违反"as-if"规则; 这就是所有转换应该产生一个语义上等效的代码.
但是,超出C++级别,当代码优化时,可能会出现更多机会.
因此,它实际上取决于对象本身:如果move-constructors/destructors有副作用,并且(de)分配内存是副作用,那么优化就不会发生.如果仅使用POD,使用默认的move-constructors/destructors,那么它可能会自动优化.
但是如果以后没有使用变量,那么C++ 11编译器是否会优化代码,因此变量被认为是rvalues,两个版本的工作方式完全相同?
这是可能的,但它很大程度上取决于您的类型.请考虑以下带有POD类型的示例point:
#include <cstdio>
struct point {
int x;
int y;
};
static point translate(point p, int dx, int dy) {
return { p.x + dx, p.y + dy };
}
static point mirror(point p) {
return { -p.x, -p.y };
}
static point make_point(int x, int y) {
return { x, y };
}
int main() {
point a = make_point(1, 2);
point b = translate(a, 3, 3);
point c = mirror(b);
std::printf("(x,y) = (%d,%d)\n", c.x, c.y);
}
Run Code Online (Sandbox Code Playgroud)
我查看了汇编代码,这里是整个程序(!)基本编译成的内容(所以下面的代码是生成的汇编代码的C近似值):
int main() {
std::printf("(x,y) = (-4,-5)\n");
}
Run Code Online (Sandbox Code Playgroud)
它不仅摆脱了所有的局部变量,而且还在编译时进行了计算!我尝试过gcc和clang但不是msvc.
好吧,让我们让程序变得更复杂,以便它不能进行计算:
int main(int argc, char* argv[]) {
int x = *argv[1]-'0';
int y = *argv[2]-'0';
point a = make_point(x,y);
point b = translate(a, 3, 3);
point c = mirror(b);
std::printf("(x,y) = (%d,%d)\n", c.x, c.y);
}
Run Code Online (Sandbox Code Playgroud)
要运行此代码,您必须将其称为./a.out 1 2.
整个程序在优化后简化为此程序(在C中重写的程序集):
int main(int argc, char* argv[]) {
int x = *argv[1]-'0';
int y = *argv[2]-'0';
std::printf("(x,y) = (%d,%d)\n", -(x+3), -(y+3));
}
Run Code Online (Sandbox Code Playgroud)
所以它摆脱了a, b, c所有的功能make_point(),translate()并且mirror()在编译时尽可能多地完成了计算.
由于Matthieu M.的回答中提到的原因,不要期望在更复杂的类型(特别是非POD)中进行如此好的优化.
根据我的经验,内联是至关重要的.努力工作,以便您的功能可以轻松内联.使用链接时优化.
请注意,除了可以大大加速代码的移动语义之外,编译器还在执行(N)RVO - (命名)返回值优化,这实际上可以为代码提供更高的效率.我已经测试了你的例子,在g ++ 4.8中看来你的第二个例子实际上可能是更优的:
object3 a(x);
object2 b(a);
object1 c(b);
return c;
Run Code Online (Sandbox Code Playgroud)
从我的实验看起来它会调用构造函数/析构函数8次(1 ctr + 2 copy ctrs + 1 move ctr + 4 dtrs),相比之下调用它10次的其他方法(1 ctr + 4 move ctors + 5 dtors) .但是正如user2079303所评论的那样,移动构造函数仍然应该优于复制构造函数,在这个例子中,所有调用都将被内联,因此不会发生函数调用开销.
复制/移动省略实际上是"as-if"规则的一个例外,这意味着有时你可能会惊讶你的构造函数/析构函数甚至没有副作用的东西也不会被调用.
http://coliru.stacked-crooked.com/a/1ca7ebec0567e48f
(您可以使用-fno-elide-constructors参数禁用(N)RVO)
#include <iostream>
#include <memory>
template<int S>
struct A {
A() { std::cout<<"A::A"<<std::endl; }
template<int S2>
A(const A<S2>&) { std::cout<<"A::A&"<<std::endl; }
template<int S2>
A(const A<S2>&&) { std::cout<<"A::A&&"<<std::endl; }
~A() { std::cout<<"~A::A"<<std::endl;}
};
A<0> foo () {
A<2> a; A<1> b(a); A<0> c(b); return c; // calls dtor/ctor 8 times
//return A<0>(A<1>(A<2>())); // calls dtor/ctor 10 times
}
int main()
{
A<0> a=foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2092 次 |
| 最近记录: |