2015-12-27 31 views
2

箭头解引用p->m(*p).m的语法糖,它看起来像它可能涉及两个单独的内存查找操作 - 一个找到堆上的对象,第二个找到成员字段偏移。C++重复箭头操作符解引用性能vs点运算符

这让我质疑这两个代码片段之间是否存在任何性能差异。假设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... 
} 

所以我的问题是这两个版本之间是否存在性能差异(即使非常小),或者如果编译器是s (即使不同的字段访问被它们之间的许多其他代码行中断,我没有在这里显示)。

+1

如果没有人知道答案,我将不得不微调自己的基准。不过,我希望将来可以为其他人提供答案。 –

+0

测量它...或者比较程序集的输出,你甚至可以在线完成https://gcc.godbolt.org/ – StoryTeller

+0

好吧,只要你自己找到答案就可以了您的代码或使用分析器。 – johnbakers

回答

2

箭头解除引用对 - >米为(* P).M

即通常不是真的,但是在有限的上下文中,你所要求真实语法糖。一个上找到堆和第二到接着 定位构件字段偏置对象 -

出现像它可能涉及两个独立的存储器查找 操作

根本不是。它是一个读取持有指针的参数或局部变量,另一个读取成员。但是任何合理的优化器都会将指针保留在您显示的代码中的寄存器中,因此无需额外访问。

但您的替代版本也有一个本地指针,所以没有差别反正(在方向上至少你问):

classA &a = *ptr; 

假设整体功能没有被内联或承担一些其他原因编译器不知道到底哪里ptr分,&必须为指针,因此无论是编译器可以推断它是安全的a是的*ptr别名所以NO差,或编译器必须做a别名*copy_of_ptr所以ver sion使用&会比复制ptr的成本慢(不像预期的那样快)。

即使不同领域的访问是通过它们之间的其他 多行代码,这是我没有在这里显示

那你移动朝有趣的案例中断。如果该干预代码可能会改变ptr那么显然这两个版本的行为不同。但是如果人类可以看到介入代码不能更改ptr,而编译器无法看到:那么这两个版本在语义上是相等的,但编译器不知道,编译器可能会生成较慢您尝试通过创建参考进行手动优化版本的代码。

+0

谢谢,你的回答是有道理的。我猜我的心理呃逆没有意识到*版本2 *仍然需要与*版本1 *完全相同的指针查找。你是正确的,在最好的情况下,*版本2 *将“速度”*版本1 *,在最坏的情况下,它可能会稍微慢一点。 –

2

大多数(?all)编译器在引擎下实现引用作为指针,所以我期望在生成的程序集中没有区别(除了可能的副本来初始化引用 - 但我希望优化器消除即使这样) 。

一般来说,这种微型优化是不值得的。总是倾向于专注于清晰和正确的代码。这是当然不值得这种优化,直到你测量了瓶颈在哪里。

+1

大多数编译器在引擎盖下实现**引用的困难**使用。引用的简单使用是作为编译时间别名来实现的。 'int &a=x;''a'在编译时可能只是'x'的另一个名字。 'a'可能不需要它自己的任何实现。 – JSF

+0

Ish。指针也是如此。 'int * p =&x;''* p'在编译时可能只是'x'的另一个名字,'p'可能不需要它自己的任何实现。 –