C++编译优化RVO&NRVO&复制省略

转载原文:https://www.cnblogs.com/kekec/p/11303391.html

编译器优化技术

现代编译器缺省会使用 RVO(return value optimization,返回值优化)、NRVO(named return value optimization、命名返回值优化)和复制省略(Copy elision)技术,来减少拷贝次数来提升代码的运行效率。

注1:vc6、vs 没有提供编译选项来关闭该优化,无论是 debug 还是 release 都会进行 RVO 和复制省略优化

注2:vc6、vs2005 以下及 vs2005 + Debug 上不支持 NRVO 优化,vs2005 + Release 支持 NRVO 优化

注3:g++ 支持这三种优化,并且可通过编译选项:-fno-elide-constructors 来关闭优化。

RVO 优化

如下程序:

#include <stdio.h>
class A
{
public:
    A()
    {
        printf("%p construct\n", this);
    }
    A(const A& cp)
    {
        printf("%p copy construct\n", this);
    }
    ~A()
    {
        printf("%p destruct\n", this);
    }
};

A GetA()
{
    return A();
}

int main()
{
    {
        A a = GetA();
    }
    return 0;
}

在 g++ 和 vc6、vs 中,上述代码仅仅只会调用一次构造函数和析构函数 ,输出结果如下:

0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct

在 g++ 中,加上 -fno-elide-constructors 选项关闭优化后,输出结果如下:

0x7ffc46947d4f construct  // 在函数GetA中,调用无参构造函数A()构造出一个临时变量temp
0x7ffc46947d7f copy construct // 函数GetA return语句处,把临时变量temp做为参数传入并调用拷贝构造函数A(const A& cp)将返回值ret构造出来
0x7ffc46947d4f destruct // 函数GetA执行完return语句后,临时变量temp生命周期结束,调用其析构函数~A()
0x7ffc46947d7e copy construct // 函数GetA调用结束,返回上层main函数后,把返回值变量ret做为参数传入并调用拷贝构造函数A(const A& cp)将变量A a构造出来
0x7ffc46947d7f destruct // A a = GetA()语句结束后,返回值ret生命周期结束,调用其析构函数~A()
0x7ffc46947d7e destruct // A a要离开作用域,生命周期结束,调用其析构函数~A()

注:临时变量 temp、返回值 ret 均为匿名变量。

下面用 C++ 代码模拟一下其优化行为:

#include <new>
A& GetA(void* p)
{
    //由于p的内存是从外部传入的,函数返回后仍然有效,因此返回值可为A&
    //vs中,以下代码还可以写成:
    // A& o = *((A*)p);
    // o.A::A(); 
    // return o;
    return *new (p) A(); // placement new
}

int main()
{
    {
        char buf[sizeof(A)];
        A& a = GetA(buf);
        a.~A();
    }
    return 0;
}

NRVO 优化

g++ 编译器、vs2005 + Release(开启 O2 及以上优化开关)

修改上述代码,将 GetA 的实现修改成:

A GetA()
{
    A o;
    return o;
}

在 g++、vs2005 + Release 中,上述代码也仅仅只会调用一次构造函数和析构函数 ,输出结果如下:

0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct

g++ 加上 -fno-elide-constructors 选项关闭优化后,和上述结果一样:

0x7ffc46947d4f construct
0x7ffc46947d7f copy construct
0x7ffc46947d4f destruct
0x7ffc46947d7e copy construct
0x7ffc46947d7f destruct
0x7ffc46947d7e destruct

但在 vc6、vs2005 以下、vs2005 + Debug 中,没有进行 NRVO 优化,输出结果为:

18fec4 construct  // 在函数GetA中,调用无参构造函数A()构造出一个临时变量o
18ff44 copy construct  // 函数GetA return语句处,把临时变量o做为参数传入并调用拷贝构造函数A(const A& cp)将返回值ret构造出来
18fec4 destruct  // 函数GetA执行完return语句后,临时变量o生命周期结束,调用其析构函数~A()
18ff44 destruct // A a要离开作用域,生命周期结束,调用其析构函数~A()

下面用 C++ 代码模拟一下 vc6、vs2005 以下、vs2005 + Debug 上的行为:

#include <new>
A& GetA(void* p)
{
    A o;
    //由于p的内存是从外部传入的,函数返回后仍然有效,因此返回值可为A&
    //vs中,以下代码还可以写成:
    // A& t = *((A*)p);
    // t.A::A(o); 
    // return t;
    return *new (p) A(o); // placement new
}

int main()
{
    {
        char buf[sizeof(A)];
        A& a = GetA(buf);
        a.~A();
    }
    return 0;
}

注:与 g++、vs2005 + Release 相比,vc6、vs2005 以下、vs2005+ Debug 只优化掉了返回值到变量 a 的拷贝,命名局部变量 o 没有被优化掉,所以最后一共有 2 次构造和析构的调用。

复制省略优化

典型情况是:调用构造函数进行值类型传参

void Func(A a) 
{
}

int main()
{
    {
        Func(A());
    }
    return 0;
}

在 g++ 和 vc6、vs 中,上述代码仅仅只会调用一次构造函数和析构函数 ,输出结果如下:

0x7ffeb5148d0f construct
0x7ffeb5148d0f destruct

在 g++ 中,加上 -fno-elide-constructors 选项关闭优化后,输出结果如下:

0x7ffc53c141ef construct   // 在main函数中,调用无参构造函数构造实参变量o
0x7ffc53c141ee copy construct // 调用Func函数后,将实参变量o做为参数传入并调用拷贝构造函数A(const A& cp)将形参变量a构造出来
0x7ffc53c141ee destruct // 函数Func执行完后,形参变量a生命周期结束,调用其析构函数~A()
0x7ffc53c141ef destruct // 返回main函数后,实参变量o要离开作用域,生命周期结束,调用其析构函数~A()

下面用 C++ 代码模拟一下其优化行为:

void Func(const A& a)
{
}

int main()
{
    {
        Func(A());
    }
    return 0;
}

优化失效情况

开启 g++ 优化,得到以下各种失效情况的输出结果:

  • 根据不同的条件分支,返回不同变量
A GetA(bool bflag)
{
    A a1, a2;
    if (bflag)
        return a1;
    return a2;
}

int main()
{
    A a = GetA(true);
    return 0;
}

结果如下:

0x7ffc3cca324f construct
0x7ffc3cca324e construct
0x7ffc3cca327f copy construct
0x7ffc3cca324e destruct
0x7ffc3cca324f destruct
0x7ffc3cca327f destruct

注1:2 次缺省构造函数调用:用于构造 a1、a2

注2:1 次拷贝构造函数调用:用于拷贝构造返回值

注3:这里仍然用右值引用优化掉了一次拷贝函数调用:返回值赋值给 a

  • 返回参数变量

  • 返回全局变量

  • 返回复合数据类型中的成员变量

  • 返回值赋值给已构造好的变量(此时会调用 operator== 赋值运算符)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值