…interface {}函数参数中的“取消引用”元素

I have a logger that works fine, but produces quite some overhead in regard to memory allocation. Below Debug() function is not printing on purpose, because logOutputLevel isn't high enough.

var logOutputLevel = 2
func Debug(s string, args ...interface{}) {
    if logOutputLevel > 1 { return }
    fmt.Printf(s, args...)
}

Still that method produces quite some allocs when passing values to it. It doesn't produce heavy allocs when passing pointers to it. See the following benchmarks:

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)
    }
}

Produces:

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

Now that's all nice and I'm trying to rework the Debug() method to accept one string and unlimited pointer arguments only. At a later point I would like to 'dereference' all arguments and pass them to fmt.Printf() if the loglevel is high enough.

How can I achieve this? Is there a specific language idiom for "only pointers"? I assume that ...*interface{} means a pointer to an interface{} (and not any values should be a pointer).

The only way to prevent the allocations is to not do them in the first place.

This is usually done by putting the debug statement in a conditional block before it's evaluated:

if logOutputLevel > 1 {
    Debug("test: %s", x)
}

Which is how most logging packages handle it. See the glog Verbose type for example.

You can use build tags to conditionally compile the Debug function, and ignore the arguments altogether. This isn't guaranteed by the language spec to not allocate, but it is an optimization that the compiler could possibly make in the future, if the current performance it acceptable. Using two separate files, you can switch between the Debug implementations at compile time:

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{}) {}