性能大杀器: c++中的copy elision

程序员咋不秃头2024-05-26 21:39:55  103

在上一篇文章性能大杀器:std::move 和 std::forward中,我们简单的介绍了下移动语义,今天聊聊编译器的一个常见优化拷贝消除(copy elision)。

move和copy elision是一种常见的编译器优化技术,旨在避免不必要的临时对象的复制和拷贝,对于那种占用资源比较多的对象来说,这种优化无疑会很大程度上提升性能。

且看一个例子,如下:

#include struct Obj { Obj { std::cout << "Default ctor" << std::endl; } Obj(const Obj& r) { std::cout << "Copy ctor" << std::endl; } int x_ = 0;};Obj CreateObj1 { return Obj;}Obj CreateObj2 { Obj temp; temp.x_ = 42; return temp;}int main { Obj o1(CreateObj1); Obj o2(CreateObj2); return 0;}

编译并运行上述代码,输出:

Default ctorDefault ctor

PS:本文中所使用的编译器及版本为gcc 11.4.0,如果未做显式说明,在编译过程中都加上-std=c++11选项。

好了,仍然是上面的代码,如果编译选项变成-std=c++11 -fno-elide-constructors,输出试试,看看会是什么结果~~

emm,在本地尝试编译并运行了下:

Default ctorCopy ctorCopy ctorDefault ctorCopy ctorCopy ctor

与最开始的输出相比,多了很多,现在我们着手分析下原因,以Obj o1(CreateObj1);为例:

?调用CreateObj1函数,创建一个临时对象并返回,此时会输出Default ctor?将上述的需要返回的临时对象以拷贝方式赋值给函数返回值,此时会输出Copy ctor?函数返回值作为obj1的拷贝对象,此时会输出Copy ctor

接着分析下Obj o2(CreateObj2);:

?CreateObj2创建一个临时变量temp,此时会输出Default ctor?修改临时变量temp的成员变量x_的值为2?temp以拷贝方式赋值给函数返回值,此时会输出Copy ctor?函数返回值作为obj2的拷贝对象,此时会输出Copy ctor

对前面的输出做个简单总结,如下:

Default ctor // 在CreateObj1中以Obj方式创建临时变量T1Copy ctor // T1以复制拷贝的方式赋值给CreateObj1函数返回值,此处假设为T2Copy ctor // 通过调用拷贝构造函数,将T2值赋值给o1Default ctor // 创建临时变量tempCopy ctor // temp以复制拷贝的方式赋值给CreateObj1函数返回值,此处假设为temp2Copy ctor // 通过调用拷贝构造函数,将temp2值赋值给o2

在上一节中,我们提到过,可以通过使用移动构造的方式来避免拷贝,为了测试该功能,尝试在Obj类中新增一个移动构造函数:

#include struct Obj { Obj { std::cout << "Default ctor" << std::endl; } Obj(const Obj& r) { std::cout << "Copy ctor" << std::endl; } Obj(const Obj&& r) { // 移动构造函数 std::cout << "Move ctor" << std::endl; } int x_ = 0;};Obj CreateObj1 { return Obj;}Obj CreateObj2 { Obj temp; temp.x_ = 42; return temp;}int main { Obj o1(CreateObj1); Obj o2(CreateObj2); return 0;}

输出如下(编译选项为-std=c++11 -fno-elide-constructors):

Default ctorMove ctorMove ctorDefault ctorMove ctorMove ctor

看了上述输出,不禁奇怪,为什么在CreateObj2函数中,创建的temp明明是一个左值,此处却调用的是移动构造即当做右值使用呢?,我们不妨看看标准对此处的解释:

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler (14.4)) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call’s return object

意思是当返回语句中的表达式是一个非volatile的命名对象,其类型与函数的返回类型相同时,编译器可以优化掉拷贝或移动操作,直接将自动对象构造到函数调用的返回对象中。

这意味着,当函数返回一个自动对象时,编译器可以优化掉不必要的拷贝或移动操作,直接将自动对象构造到函数调用的返回对象中,以提高效率。这种优化在 C++ 标准中被明确规定,以支持更高效的代码生成。

标准的这一规定,使得原本不支持拷贝的对象,作为函数返回值时,也成了可能。

众所周知,std::unique_ptr<>不支持拷贝操作,即:

std::unique_ptr p1 = std::make_unique(1);std::unique_ptr p2 = p1;

上述代码将编译失败,错误提示如下:

error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete]' std::unique_ptr p2 = p1;note: declared here unique_ptr(const unique_ptr&) = delete;

那么,如果将其作为函数返回值呢?

std::unique_ptr CreateUnique { auto ptr = std::make_unique(0); return ptr;}int main { CreateUnique; return 0;}

编译正确,进一步证明了我们前面的说法。

好了,如果我们在编译选项中去掉-fno-elide-constructors,那么输出如下:

Default ctorDefault ctor

通过这个输出,可以看出,编译器忽略了拷贝构造函数的调用,而是直接构造o1和o2对象,这种方式在性能上有了很大的提升,编译器对o1和o2的这种优化方式称为RVO和NRVO。

现在,我们仔细回想下前面的示例代码,在编译的时候,都加上了-std=c++11这个选项,这是因为笔者的gcc11.4默认情况下是用的c++17,而c++17是能够保证RVO优化的,单独对NRVO则不能保证。

如果使用g++ test.cc -o test编译并运行代码,输出:

Default ctorDefault ctor

以上~~

转载此文是出于传递更多信息目的。若来源标注错误或侵犯了您的合法权益,请与本站联系,我们将及时更正、删除、谢谢。
https://www.414w.com/read/655488.html
0
随机主题
运输利勃海尔956现货金价短期仍面临回调修正德甲保级推送: 波鸿VS杜塞尔多夫, 不败金身再添一局, 德甲升降机名不虚传!6.18电视超详细选购指南 快来抄作业!山海有约 海西州来甬推介! “浙青一家亲·共筑山海情”文化走亲宁波专场举行问界M7又要上新 都有哪些变化反制说到做到, 中方拉长清单, 外交部宣布出手, 美国政客财路被断比亚迪“廉价腾势D9”要来了, 中大型MPV, 或20多万级!手机弹出“系统更新”提示, 大多数人会这样做! 看看老师傅怎么说吞噬星空大改, 界中界血腥混战, 罗峰抱大腿, 雾大人杀疯了爆笑漫画《阿U校园爆笑王》、免费奶茶、雪极星滑雪体验券, 橙柿福利爆款上新 | 橙柿福利亚当·兰伯特的中国改造: 告别高跟鞋, 美甲, 欢迎甜心版张震岳!新加坡波音777客机遭严重乱流急降1死30伤 空乘和上厕所者伤势最重扛不住了?乌总统呼吁西方参战,乌民众请愿:征召泽连斯基入伍!他曾担任煤炭工业部部长, 奉命组建神华集团, 留下了宝贵精神财富江西南康给佛山下战书, 《人民日报》火速点赞, 这一战有意思了铃木UY125改装“曼城太子爷——福登”贪财又贪色! 德不配位的4位老戏骨, “晚节不保”真的一点都不冤越南经济崩盘! 重蹈日本80年代覆辙, 或成为亚洲第一个倒下的国家泰消保风险提示: 利率下行时期, 这样选择保险, 稳稳守住你钱袋子
最新回复(0)