繁体   English   中英

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

[英]Passing scalar types by value or reference: does it matter?

当然, 微优化是愚蠢的 ,可能是实践中出现许多错误的原因。 尽管如此,我看到很多人做了以下事情:

void function( const double& x ) {}

代替:

void function( double x ) {}

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

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

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

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

在这种情况下通过引用传递本身肯定不是更有效。 请注意,使用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 ) {

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

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

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

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

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

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


*)如果不是这样,请参阅对此的评论和SOReader对发生的事情的回答。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM