【问题标题】:Dot operator cost c/c++ [closed]点运算符成本 c/c++ [关闭]
【发布时间】:2011-03-27 06:39:29
【问题描述】:

我们都知道 -> 与 .在 c/c++ 中访问成员的速度差异,但我很难找到简单点运算符的实际成本的任何线索。

我想它类似于结构地址 + 偏移量,还假设偏移量是所有先前成员的所有 sizeof-s 的总和。这(大致)正确吗?

那么与->相比,谁快得多?两次?
(在 SO 上看到了一些 asm,关于 .access 是一条指令,我想它有一些魔力)

另外,与局部变量相比,慢多少?

谢谢

编辑:
我猜我没问对。

尝试清理:
通过“-> vs.”我的意思是“使用指针访问结构”与“直接成员访问” - (link)。
然后我很好奇:“好吧,点访问本身呢?它确实要花一些钱。”所以我问了这个问题。

“点运算符成本 c/c++”本身可能是荒谬/无意义/幼稚的问题,但它确实得到了我正在寻找的答案。现在不能说更好了。

谢谢

【问题讨论】:

  • 完全依赖于实现,C++ 是一种语言而不是输出。观察您的特定编译器给出的输出。当然,我们应该清楚,这些信息并不规定任何设计选择,因为您应该选择更清洁,而不是更快(在微优化级别)。
  • 我不明白为什么. operator 和'-> operator` 之间应该有任何区别。关于我能想到的唯一区别是引用的局部性,因此变量更有可能在缓存中(但这不是您要考虑的)。

标签: c++ c dot-operator


【解决方案1】:

我们都知道 -> 与 .在 c/c++ 中访问成员的速度差异,但我很难找到简单点运算符的实际成本的任何线索。

“我们所有人”显然不包括我。我不知道 ->. 之间有任何显着差异(尤其是在 C++ 中)。

我想它类似于结构地址 + 偏移量,还假设偏移量是所有先前成员的所有 sizeof-s 的总和。这(大致)正确吗?

是的。

那么与->相比,它快了多少呢?两次? (在 SO 上看到了一些 asm,关于 . 访问是一条指令,我想它有一些魔力)

->. 都涉及计算有效地址,这是最昂贵的操作(除了实际的内存访问)。如果指针(在-> 的左侧)经常使用(例如this),那么它很可能已经被编译器缓存在CPU 寄存器中,有效地否定了->. 之间的任何可能差异.

嗯,this 是一个指针,属于方法内对象的所有内容实际上都以 this-> 为前缀,但 C++ 程序并没有慢到爬行。

显然,如果 . 应用于引用,则它 100% 等效于 ->

另外,与局部变量相比,它慢了多少?

很难评估。 meta assembler 的本质区别是:两个 asm ops 访问局部变量(将变量在堆栈上的偏移量添加到堆栈指针;访问值)与三个 asm ops 访问属性通过指针访问对象(加载指向对象的指针;添加偏移量;访问值)。但由于编译器优化,这种差异很少被注意到。

局部变量和全局变量之间的区别通常很突出:必须计算局部变量/对象属性的地址,而所有全局变量在链接时都有唯一的全局地址。

但是字段/属性访问的开销与例如系统调用的开销。

【讨论】:

  • "显然,如果 . 应用于引用,则它 100% 等效于 ->。"嗯,不是很明显。考虑int i; int& j = i; j = 5;,我认为您很难找到生成与int i; int * const j = &i; *j = 5; 相同代码的编译器。引用不一定是“简单”的指针。不过还是 +1,指出 this-> 很好。
  • 因为变量在堆栈上而突出并不意味着您可以错过load the pointer to the object 步骤。
  • @Martin: "栈上的变量" == "栈上对象的属性",因为没有指向对象的指针。可以在编译时计算属性的偏移量(相对于堆栈帧的开始)。
  • @Dummy00001:绝对不是真的。您需要知道变量的地址而不是属性。您调用的任何方法都需要知道 this 指针,因此无法避免此步骤。因此访问局部变量的成本与通过指针访问的成本完全相同(因为您必须获取指向局部变量的指针)。试试看。
  • @Martin:“因此,访问局部变量的成本与通过指针访问的成本完全相同(因为您必须获取指向局部变量的指针)。”您忘记了指针也必须保存在某个地方。 int a; a; - 你通过堆栈帧 + 堆栈帧内的 a 偏移量到达 aint *a; *a; - 您首先必须获取 a(作为局部变量),然后才能访问 *a
【解决方案2】:

任何体面的编译器都会在编译时计算struct 字段的地址,因此. 的成本应该为零。

换句话说,使用. 访问struct 字段与访问变量一样昂贵。

【讨论】:

  • 结构地址和求和的计算/加载怎么样?
  • @Basilevs 问些有意义的问题怎么样?
  • 这不是一个真正的问题。我想说的是您对点运算符成本的估计基本上是不正确的:)
  • Dummy00001 已经解释了至少应该读取这个指针并计算成员地址以使用点运算符。即使这个指针被缓存,这仍然需要至少一个 CPU 滴答声。更可能是两个。
  • @Basilevs:或者零。许多指令集架构(包括 x86)包含内存访问指令的偏移寻址模式。这意味着如果您在寄存器中有 address,则可以使用“读取 (address + x)”指令。在这种情况下,偏移量基本上是免费的。
【解决方案3】:

这取决于很多事情。

. 运算符可能比访问局部变量或使用 -> 更便宜,也可能比两者都贵。

这不是一个明智的问题。

【讨论】:

  • 在什么情况下通过.访问对象中的成员会比通过指针和->访问更昂贵?在某些情况下使用引用/指针时它可能是等价的,但我想不出在任何情况下它可能会更昂贵。
  • 如果->访问栈上的对象,.访问堆上的对象并触发缺页,->会比.便宜很多。这就是为什么我说这不是一个明智的问题 - 答案取决于对象的存储位置,而不是用于访问它的语法。
【解决方案4】:

我想说成本的差异不在于运算符本身,而在于访问左侧对象的成本。

ptr->variable

应该产生与

类似的 asm 输出
(*ptr).variable // yeah I am using a '.' because it's faster...

因此,您的问题有点荒谬。

我想我明白你的意思,所以我会尝试回答这个问题。

运营商本身几乎没有成本。它们只涉及计算地址。该地址表示为对象的地址加上偏移量(可能在编译时固定,因此在程序中每个字段都是一个常量)。真正的成本来自实际获取该地址的字节。

现在,我不明白为什么使用 ->. 的成本更高,因为它们实际上做同样的事情,但在这种情况下访问权限可能会有所不同:

struct A { int x; };

void function(A& external)
{
  A local;

  external.x = local.x;
}

在这种情况下,访问external.x 的成本可能更高,因为它需要访问函数范围之外的内存,因此编译器无法提前知道内存是否已经被取出并放入处理器缓存(或寄存器)等...

另一方面,local.x 是本地的并存储在堆栈(或寄存器)中,编译器可能会优化掉代码的获取部分并直接访问 local.x

但是您注意到使用->. 之间没有区别,区别在于使用局部变量(存储在堆栈中)与使用指向外部提供的对象的指针/引用,在该对象上编译器无法做出假设。

最后,需要注意的是,如果函数是内联的,那么编译器可以在调用者的站点优化它的使用,并在那里有效地使用稍微不同的实现,但无论如何都不要尝试内联所有内容:首先编译器可能会忽略你的提示,如果你真的强迫它,你实际上可能会失去性能。

【讨论】:

    猜你喜欢
    • 2012-09-24
    • 2011-03-26
    • 2015-01-07
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多