我有一个工作正常的记录器,但在内存分配方面产生了相当大的开销。 Debug()
下面的功能不是有意打印的,因为logOutputLevel
不够高。
var logOutputLevel = 2
func Debug(s string, args ...interface{}) {
if logOutputLevel > 1 { return }
fmt.Printf(s, args...)
}
仍然,该方法在将值传递给它时会产生很多分配。将指针传递给它时,它不会产生繁重的分配。请参阅以下基准:
func BenchmarkLog(b *testing.B) {
x := "abc"
for n := 0; n < b.N; n++ {
Debug("test %s", x)
}
}
func BenchmarkLogRef(b *testing.B) {
x := "abc"
for n := 0; n < b.N; n++ {
Debug("test %s", &x)
}
}
产生:
BenchmarkLog-8 50000000 43.1 ns/op 16 B/op 1 allocs/op
BenchmarkLogRef-8 500000000 3.17 ns/op 0 B/op 0 allocs/op
现在一切都很好,我正尝试重新设计Debug()
方法,使其仅接受一个字符串和无限的指针参数。稍后,如果日志级别足够高,我想“取消引用”所有参数并将它们传递给fmt.Printf()
。
我该如何实现?是否有针对“仅指针”的特定语言习语?我假设...*interface{}
是指向interface{}
的指针(并非任何值都应该是指针)。
答案 0 :(得分:1)
防止分配的唯一方法是首先不要进行分配。
这通常是通过在评估之前将debug语句放在条件块中来完成的:
if logOutputLevel > 1 {
Debug("test: %s", x)
}
大多数日志记录程序包将如何处理它。例如,请参见glog Verbose
type。
您可以使用构建标记有条件地编译Debug
函数,而完全忽略参数。语言规范不能保证不分配这种情况,但是如果当前的性能可以接受,这是编译器将来可能会进行的优化。使用两个单独的文件,您可以在编译时在Debug
实现之间进行切换:
debug.go
// +build debug
package main
import "log"
func Debug(fmt string, args ...interface{}) {
log.Printf(fmt, args...)
}
release.go
// +build !debug
package main
func Debug(_ string, _ ...interface{}) {}