【问题标题】:Simple function is not inlined简单函数没有内联
【发布时间】:2020-02-26 15:28:06
【问题描述】:

我正在使用 Prometheus 向我的 Go 程序添加指标调用。为了可维护性,我决定将所有 Prometheus 调用与简单函数调用分开放在一个单独的源文件中(以防我想移动到不同的指标包)。但更重要的是,它还可以更快地编写代码,因为 IDE 将提示标签名称作为函数调用的参数。比如这样的:

var requestCounter = promauto.NewCounterVec(prometheus.CounterOpts{}, []string{"name"})

func incrementRequestCounter(label1, label2 string) {
    requestCounter.WithLabelValues(label1, label2).Inc()
}

其中一些函数经常在低级循环中调用,因此我不希望这些调用过多地减慢代码速度。我的假设是这样一个简单的代码行很容易内联。但是检查(使用构建选项--gcflags -m)我发现上面的单行函数没有内联(go1.12.5 windows/amd64)。有谁知道为什么?以及如何解决这个问题?注意这个函数是内联的:

func incrementRequestCounter(label1, label2 string) {
    requestCounter.WithLabelValues(label1, label2)
}

通过进一步的实验,如果一个函数对-inlineable 函数的调用不止一次,它似乎不会被内联。 (您可以多次调用可内联函数,而一个函数仍然是可内联的。)

【问题讨论】:

  • 如果您实际测量的性能问题已缩小到此处的函数调用开销,那么您的问题中没有任何迹象。这似乎是过早的优化。
  • 内联代码的能力总是在变化,所以对于 Go 开发人员来说,这可能是一个比这里更好的问题。堆栈中内联本身是相当新的,虽然您的函数可能看起来“简单”,但实际复杂性仍然取决于对其调用的所有函数的分析。
  • @Adrian 我对代码进行了基准测试,您认为函数调用开销可以忽略不计是正确的。我已经学会了先进行基准测试!但我也发现 Prometheus 调用比我想象的要慢一些——幸运的是我发现很容易将它们移到紧密的内部循环之外(使用 Add() 而不是 Inc())。

标签: go inline


【解决方案1】:

只需发布具有以下几点的答案(因为没有其他人有):

  1. 在尝试优化之前进行基准测试。
  2. 看似简单的函数可能很难内联
  3. 内联正在发展,未来可能会内联

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    • 1970-01-01
    相关资源
    最近更新 更多