【问题标题】:Go, encapsulation and perfomanceGo、封装和性能
【发布时间】:2020-12-17 10:35:35
【问题描述】:

我正在探索的代码:

type Stack struct {
    length int
    values []int
}

func (s *Stack) Push(value int) {
    // ...
}

func (s *Stack) Pop() int {
    // ...
}

func (s *Stack) Length() int {
    return s.length
}

方法PushPop 更改Stack 结构中的length 字段。我想从其他文件中隐藏这个字段以防止像stack.length = ...这样的代码(手动length更改)。但是我需要有能力阅读这个领域,所以我添加了getter方法 - Length

我的问题是:

stack.Length() 不应该比stack.length 慢,因为它是一个函数调用吗?我学了一点汇编,我知道程序调用一个函数应该做多少操作。我是否理解正确:通过添加 getter 方法 stack.Length() 我保护了那些使用我的库的人免受不良使用但它的成本 - 程序的性能?这实际上不仅涉及 Go。

【问题讨论】:

  • 我向您保证,如果存在差异,则可以忽略不计。可能没有,因为编译器可能会内联对该方法的部分或全部调用。
  • 您真的认为对静态结构字段的内联方法调用会影响(!)您的应用程序的性能吗?您是否有任何经验证据表明这是相关的?
  • @Volker 如果我这么写 - 你为什么要问“你真的这么想吗”?我解释了我这么认为的原因 - 汇编程序。如果您不明白我在说什么,请阅读 call 命令。
  • @Don2Quixote Volker 不是那个意思。彼得和沃尔克都在暗示同样的意思,即使有性能增益;它可以忽略不计。编译器做了很多优化,可能会内联。
  • 是的,你是对的,它会降低性能。但是,如果最高性能是唯一目标,那么您的实现应该如下所示:type Stack struct {}; func (s *Stack) Push(_ int) {}; func (s *Stack) Pop() int {return 0}; func (s *Stack) Length() int {return 0),因为这是最快的实现,因为它将被优化器完全删除。不幸的是,这种实现有严重的缺点。一切都是妥协,有些妥协非常容易做出,因为一方的缺点完全可以忽略不计,这就是我试图解释的原因

标签: performance go compiler-optimization


【解决方案1】:

stack.Length() 不应该比stack.length 慢吗,因为它是一个函数调用?

Objection! Assumes facts not in evidence.

具体来说:

  1. 为什么你认为它是一个函数调用?看起来像一个,但实际的 Go 编译器通常会在行中扩展代码。
  2. 为什么你认为函数调用比内联代码慢?在实际计算机上测量实际程序时,有时函数调用比内联代码。事实证明,关键部分通常是正在执行的指令及其操作数是否已经在适当的 CPU 缓存中。有时,内联扩展函数会使程序运行得更慢。

编译器应该进行内联扩展,除非它使程序运行得更慢。编译器在检测到这种减速之前或之后的表现如何(如果存在)是一个单独的问题。在这种特殊情况下,给定函数定义,编译器几乎肯定会直接扩展函数,因为访问stack.length 可能是一条指令,调用函数将是一条指令,这里决定权衡将是容易。

【讨论】:

    猜你喜欢
    • 2011-05-17
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多