【发布时间】: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())。