在我写的程序中,我必须在函数之间传递大型数据结构(图像)。我需要在不同的操作系统上尽可能快地使用我的代码(因此,我无法分析所有测试用例)。我经常有形式代码......
void foo() {
ImageType img = getCustomImage();
}
ImageType getCustomImage() {
ImageType custom_img;
//lots of code
return custom_img;
}
AFAIK,行ImageType img = getCustomImage();
将导致为img
调用复制构造函数,其中custom_img
的返回值作为其参数。维基百科说,有些编译器甚至会再次执行此操作,作为初始临时变量!
我的问题:通过使用pass by reference而不是返回值来绕过这种开销(图像的复制构造函数是昂贵的)通常会更快......
void foo() {
ImageType img;
getCustomImage(img);
}
void getCustomImage(ImageType &img) {
//code operating directly on img
}
我被告知如果编译器支持返回值优化,那么应该没有区别。这是真的?我可以(在合理范围内)假设现在这样,当速度很重要时,我应该如何构建我的程序
答案 0 :(得分:13)
您应该编写可维护的代码,编译器在大多数情况下都非常擅长为性能做正确的事情。如果你认为事情进展缓慢,那就测量一下性能,找到瓶颈之后,试着弄清楚如何改进它。
你是正确的逻辑代码触发不同的复制结构:从custom_img
到返回的临时,然后到调用者代码中的img
对象,但是事实是两份副本都将被删除。
在按值返回与 default-construct + pass-by-reference 的特定情况下,我知道实现的所有调用约定都按值返回让调用者分配内存并将一个隐藏指针传递给被调用者,这有效地实现了你想要做的事情。所以从绩效的角度来看,它们基本上是等价的。
我在过去的两篇博客文章中写过这篇文章(函数参数和返回值中的值语义):
编辑:我故意避免讨论编译器无法应用NRVO的情况,原因是任何可以引用该对象的函数f
处理:void f( T & out ) { /* code */ }
可以简单地转换为一个函数,其中NRVO对于编译器来说是微不足道的,可以通过简单的转换将值返回到:T f() { T out; /* code */ return out; }
答案 1 :(得分:1)
由于你的图像是大数据结构,我可能会建议函数应该返回指向图像的指针。您也可以使用引用(在机器级别是指针),但我认为指针更适合此目的。
我比C更熟悉C语言,所以我错了。
重要的问题是何时以及由谁将图像取消分配。
答案 2 :(得分:1)
至少如果您针对合理的典型操作系统(如Windows,MacOS,Linux或* BSD)定位合理的当前编译器,您可以依靠实施RVO / NRVO。 IOW,你必须非常努力地找到有足够差异才能关心的案例 - 或者最有可能的任何案例。
根据您使用所涉及数据的方式,如果存在速度差异,则可能与使用引用一样容易支持传递/返回对象。您可能想要阅读David Abrahams的article关于此事。
答案 3 :(得分:0)
看到“什么是更快?”这个问题,我通常建议您在编译器/环境中自己测量一下,然后找出原因。