按值或引用传递标量类型:这有关系吗?

时间:2014-01-07 21:15:17

标签: c++ reference arguments

当然,micro-optimization is stupid可能是实践中出现许多错误的原因。尽管如此,我看到很多人都会这样做:

void function( const double& x ) {}

而不是:

void function( double x ) {}

因为它被认为“效率更高”。假设function在一个程序中经常被称为荒谬,数百万次;这种“优化”是否重要?

4 个答案:

答案 0 :(得分:6)

长话短说不,特别是在大多数现代平台上,标量甚至浮点类型都通过寄存器传递。我见过的一般经验法则是128bytes作为你应该通过值传递和通过引用传递之间的分界线。

鉴于数据已经存储在寄存器中,您实际上是通过要求处理器转出缓存/内存来获取数据来减慢速度。这可能是一个巨大的打击,取决于数据所在的缓存行是否无效。

在一天结束时,它实际上取决于平台ABI和调用约定。大多数现代编译器甚至会在调优时使用寄存器传递数据结构(例如,两个短路的结构等)。

答案 1 :(得分:2)

在后一个示例中,您可以在函数调用期间保存被复制到堆栈的4B。存储双精度需要8B,而存储指针只需要4B(在32b环境中,在64b中它需要64b = 8B,所以你不保存任何东西)或者只是一个带有一点编译器支持的指针的引用。

答案 2 :(得分:2)

除非内联函数,并且取决于calling convention(以下假定基于堆栈的参数传递,现代调用约定中仅在函数具有时使用太多参数*),参数的传递和使用方式有两点不同:

  • double:(可能) 8字节大值写入堆栈并由函数读取。
  • double &double *:值位于内存中的某处(可能“接近”当前堆栈指针,例如,如果它是局部变量,但也可能位于远处的某个位置)。一个(可能) 4 8字节大指针地址(分别为32位或64位系统)存储在堆栈中,该函数需要取消引用读取值的地址。这也要求值在可寻址存储器中,而寄存器不是。

这意味着,使用引用时,传递参数所需的堆栈空间可能会略微减少。这不仅降低了内存需求,还降低了堆栈最高字节的缓存效率。使用引用时,取消引用会增加一些工作要做。

总结一下,使用大型类型的引用(比如说sizeof(T) > 32或者甚至更多)。当堆栈大小和热度在sizeof(T) > sizeof(T*)时可能已经发挥了非常重要的作用。


*)如果不是这样的话,请参阅对此的评论以及SOReader对所发生情况的回答。

答案 3 :(得分:2)

在这种情况下通过引用传递本身肯定不是更有效。请注意,使用const限定该引用并不意味着引用的对象不能更改。而且,这并不意味着函数本身不能改变它(如果裁判不是常数,那么它可以合法地使用const_cast去除那个const的函数。考虑到这一点,很明显,通过引用传递迫使编译器考虑可能的别名问题,这通常会导致在传递引用的情况下生成[显着]效率较低的代码。

为了从图片中取出可能的别名,必须用

开始后一版本
void function( const double& x ) {
  double non_aliased_x = x;
  // ... and use `non_aliased_x` from now on
  ...
}

但这样做会使首先通过引用传递的推理理由失败。

处理别名的另一种方法是使用某种C99风格的restrict限定符

void function( const double& restrict x ) {

但同样,即使在这种情况下,通过引用传递的缺点也可能超过专业人士,正如其他答案中所解释的那样。