通过值或引用传递标量类型:这有关系吗
Passing scalar types by value or reference: does it matter?
诚然,微优化是愚蠢的,可能是实践中许多错误的原因。尽管如此,我还是看到很多人在做以下事情:
void function( const double& x ) {}
而不是:
void function( double x ) {}
因为它被认为"更有效率"。说function
在一个节目中被调侃的次数高达数百万次;这种"优化"有意义吗?
长话短说不,尤其是在标量甚至浮点类型通过寄存器传递的大多数现代平台上。我看到的一般经验法则是128字节,这是按值传递和按引用传递之间的分界线。
考虑到数据已经存储在寄存器中,您实际上是在通过要求处理器去缓存/内存获取数据来放慢速度。这可能是一个巨大的打击,取决于数据所在的缓存行是否无效。
归根结底,这实际上取决于平台ABI和调用约定是什么。当优化出现时,大多数现代编译器甚至会使用寄存器来传递适合的数据结构(例如,两个short的结构等)。
在这种情况下通过引用传递本身肯定不会更高效。请注意,使用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在函数调用期间被复制到堆栈的时间。存储doubles需要8B,存储指针只需要4B(在32b环境中,在64b环境中需要64b=8B,所以您不保存任何内容)或引用,该引用只不过是一个具有编译器支持的指针。
除非函数是内联的,并且取决于调用约定(以下假设基于堆栈的参数传递,在现代调用约定中,只有当函数有太多参数*时才使用),否则参数的传递和使用方式有两个差异:
double
:(可能)8字节的大值被写入堆栈,并由函数按原样读取double &
或double *
:该值位于内存中的某个位置(可能"靠近"当前堆栈指针,例如,如果它是局部变量,但也可能位于远处)。(可能)4或8字节大指针地址(分别为32位或64位系统)存储在堆栈上,函数需要取消引用地址来读取值。这也要求值在可寻址内存中,而寄存器不是
这意味着,在使用引用时,传递参数所需的堆栈空间可能会少一点。这不仅降低了内存需求,还降低了堆栈中最顶层字节的缓存效率。当使用引用时,取消引用会增加一些要做的工作。
总之,使用大型类型的引用(比如sizeof(T) > 32
或更多)。如果CCD_ 10。
*)如果不是这样的话,请参阅对此的评论和SOReader的答案。
- C++中的基元类型有析构函数吗?
- 基本类型与内置类型有什么区别C++
- 根据模板类型有条件地删除变量
- STDLIB中容器元素类型有什么要求?
- C++ 中的对象创建类型有什么区别?
- a、&a 和 &a[0] 之间的类型有什么区别?
- 我的编辑距离递归代码中的字符类型有问题
- C 模板:如何根据数据类型有条件编译不同的代码
- 当我有两个对象时<<如何重载运算符?(有关系)
- 为什么使用模板类型有时会出现错误 C2768,而有时我需要将类型作为指针?
- 在与时间一起使用 srand 时,如果我没有time_t参数明确地将参数转换为无符号的 int 参数,这有关系吗?
- 如果我在与字符进行比较之前没有明确将 int 转换为字符,这有关系吗?
- 原子线程围栏:为什么在这个非原子变量上存在数据竞争?这有关系吗
- 在 std::vector 中使用结构来推送多个类型有什么问题?
- VS c++和MinGW实现双重类型有什么区别
- 什么类型有多维数组
- 通过值或引用传递标量类型:这有关系吗
- c++ 中带有容器迭代器的循环类型依赖关系(GCC 失败,而 MSVC 正常)
- 实际上什么返回类型有一个新的
- C++ <Type> 和<类型*有什么区别>