c++重复箭头运算符取消引用性能与点运算符

c++ repeated arrow operator dereferencing performance vs dot operator

本文关键字:运算符 引用 性能 点运算 取消 c++      更新时间:2023-10-16

箭头取消引用p->m(*p).m的语法糖,这看起来可能涉及两个单独的内存查找操作——一个是在堆上查找对象,另一个是定位成员字段偏移量。

这让我怀疑这两个代码片段之间是否存在性能差异。假设classA有30多个不同类型的不同字段,这些字段需要按不同顺序访问(不一定连续或连续):

版本1:

void func(classA* ptr)
{
  std::string s = ptr->field1;
  int i = ptr->field2;
  float f = ptr->field3;
  // etc...
}

版本2:

void func(classA* ptr)
{
  classA &a = *ptr;
  std::string s = a.field1;
  int i = a.field2;
  float f = a.field3;
  // etc...
}

因此,我的问题是,这两个版本之间的性能是否存在差异(即使很小),或者编译器是否足够聪明,可以使它们等效(即使不同的字段访问被它们之间的许多其他代码行中断,我在这里没有显示)。

箭头取消引用p->m是(*p).m 的语法糖

这通常不是真的,但在你提出问题的有限背景下是真的。

看起来可能涉及两个单独的内存查找操作——一个用于在堆上查找对象,另一个用于定位成员字段偏移。

一点也不。一个用于读取包含指针的参数或局部变量,另一个用于访问成员。但是,任何合理的优化器都会将指针保存在您显示的代码中的寄存器中,因此没有额外的访问权限。

但你的备用版本也有一个本地指针,所以无论如何都没有区别(至少在你询问的方向上):

  classA &a = *ptr;

假设整个函数没有内联,或者由于其他原因,假设编译器不知道ptr到底指向哪里,则&必须使用指针,因此编译器可以推断出a*ptr的别名是安全的,因此存在NO差异,或者编译器必须使a成为*copy_of_ptr的别名,因此使用&的版本比复制ptr的成本慢(而不是像您预期的那样快)。

即使不同的字段访问被许多行的它们之间的其他代码,我没有在这里显示

这会让你转向有趣的案例。如果干预代码可以更改ptr,那么显然这两个版本的行为不同。但是,如果一个人能看到干预代码不能更改ptr,而编译器却看不到:那么这两个版本在语义上是相等的,但编译器不知道这一点,编译器可能会为您试图通过创建引用手动优化的版本生成较慢的代码。

大多数(?all)编译器都在后台将引用实现为指针,因此我希望生成的程序集没有任何差异(除了初始化引用的可能副本之外,但我希望优化器甚至可以消除这一点)。

总的来说,这种微优化是不值得的。它总是最好集中在清晰和正确的代码上。当然不值得这样的优化,除非你已经测量到瓶颈在哪里。