Ask questionsConsider run time performance
If we want to push this to 1.0 (#38), we really need to make some intentional decisions about performance. The API as it stands was designed largely without considering performance, and (surprise!) it shows.
glogr_benchmark_test.go:
package bench
import (
"flag"
"os"
"testing"
"github.com/go-logr/glogr"
"github.com/go-logr/logr"
"github.com/golang/glog"
)
func init() {
flag.Set("v", "1")
flag.Set("logtostderr", "true")
os.Stderr, _ = os.Open("/dev/null")
}
func BenchmarkGlogInfoOneArg(b *testing.B) {
for i := 0; i < b.N; i++ {
glog.Infof("this is a %s", "string")
}
}
func BenchmarkGlogrInfoOneArg(b *testing.B) {
var log logr.Logger = glogr.New()
for i := 0; i < b.N; i++ {
log.Info("this is", "a", "string")
}
}
func BenchmarkGlogInfoSeveralArgs(b *testing.B) {
for i := 0; i < b.N; i++ {
glog.Infof("multi: bool %t, string %s, int %d, float %f, struct %v",
true, "str", 42, 3.14, struct{ X, Y int }{93, 76})
}
}
func BenchmarkGlogrInfoSeveralArgs(b *testing.B) {
var log logr.Logger = glogr.New()
for i := 0; i < b.N; i++ {
log.Info("multi",
"bool", true, "string", "str", "int", 42,
"float", 3.14, "struct", struct{ X, Y int }{93, 76})
}
}
func BenchmarkGlogInfoV0(b *testing.B) {
for i := 0; i < b.N; i++ {
glog.V(0).Infof("multi: bool %t, string %s, int %d, float %f, struct %v",
true, "str", 42, 3.14, struct{ X, Y int }{93, 76})
}
}
func BenchmarkGlogrInfoV0(b *testing.B) {
var log logr.Logger = glogr.New()
for i := 0; i < b.N; i++ {
log.V(0).Info("multi",
"bool", true, "string", "str", "int", 42,
"float", 3.14, "struct", struct{ X, Y int }{93, 76})
}
}
func BenchmarkGlogInfoV9(b *testing.B) {
for i := 0; i < b.N; i++ {
glog.V(9).Infof("multi: bool %t, string %s, int %d, float %f, struct %v",
true, "str", 42, 3.14, struct{ X, Y int }{93, 76})
}
}
func BenchmarkGlogrInfoV9(b *testing.B) {
var log logr.Logger = glogr.New()
for i := 0; i < b.N; i++ {
log.V(9).Info("multi",
"bool", true, "string", "str", "int", 42,
"float", 3.14, "struct", struct{ X, Y int }{93, 76})
}
}
Running this:
goos: linux
goarch: amd64
pkg: github.com/go-logr/glogr/bench
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
BenchmarkGlogInfoOneArg-6 713682 1675 ns/op
BenchmarkGlogrInfoOneArg-6 251154 4761 ns/op
BenchmarkGlogInfoSeveralArgs-6 527288 2285 ns/op
BenchmarkGlogrInfoSeveralArgs-6 167934 7114 ns/op
BenchmarkGlogInfoV0-6 529798 2286 ns/op
BenchmarkGlogrInfoV0-6 164413 7293 ns/op
BenchmarkGlogInfoV9-6 46018132 25.70 ns/op
BenchmarkGlogrInfoV9-6 8412883 142.2 ns/op
So it's notably slower. All of the variadic args escape to the heap, including the string keys (which regular glog does not suffer). But doesn't account for enough.
V() calls that are not taken also expand all their variadic args used.
Some of this is attributable to glogr being very dumb and wasteful (clone() on every call) but it's not clear how much. Before we call it 1.0 we need to do some homework.
Answer
questions
thockin
Forcing noinline in the benchmark makes it more representative.
Before:
$ GO111MODULE=off go test -bench=. ./benchmark/
goos: linux
goarch: amd64
pkg: github.com/go-logr/logr/benchmark
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
BenchmarkDiscardInfoOneArg-6 25409384 54.15 ns/op
BenchmarkDiscardInfoSeveralArgs-6 8511532 146.8 ns/op
BenchmarkDiscardV0Info-6 8260953 152.7 ns/op
BenchmarkDiscardV9Info-6 8753974 147.9 ns/op
BenchmarkDiscardError-6 8291468 146.7 ns/op
BenchmarkDiscardWithValues-6 18875984 67.67 ns/op
BenchmarkDiscardWithName-6 528824234 2.077 ns/op
BenchmarkFuncrInfoOneArg-6 2606715 476.2 ns/op
BenchmarkFuncrInfoSeveralArgs-6 823752 1302 ns/op
BenchmarkFuncrV0Info-6 915470 1422 ns/op
BenchmarkFuncrV9Info-6 4300730 277.6 ns/op
BenchmarkFuncrError-6 837588 1369 ns/op
BenchmarkFuncrWithValues-6 4193628 294.4 ns/op
BenchmarkFuncrWithName-6 10532635 106.4 ns/op
After:
$ GO111MODULE=off go test -bench=. ./benchmark/
goos: linux
goarch: amd64
pkg: github.com/go-logr/logr/benchmark
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
BenchmarkDiscardInfoOneArg-6 23285353 52.58 ns/op
BenchmarkDiscardInfoSeveralArgs-6 8481985 146.9 ns/op
BenchmarkDiscardV0Info-6 7851495 144.8 ns/op
BenchmarkDiscardV9Info-6 8797778 144.8 ns/op
BenchmarkDiscardError-6 7889066 148.1 ns/op
BenchmarkDiscardWithValues-6 19125424 68.10 ns/op
BenchmarkDiscardWithName-6 500619270 2.165 ns/op
BenchmarkFuncrInfoOneArg-6 2841159 490.9 ns/op
BenchmarkFuncrInfoSeveralArgs-6 887854 1266 ns/op
BenchmarkFuncrV0Info-6 885891 1221 ns/op
BenchmarkFuncrV9Info-6 7902538 145.6 ns/op
BenchmarkFuncrError-6 852745 1401 ns/op
BenchmarkFuncrWithValues-6 4401705 295.1 ns/op
BenchmarkFuncrWithName-6 11857100 105.0 ns/op
Notably BenchmarkFuncrV9Info was cut almost in half, and there's room for
more optimization.
I will make a new push with this change.
Related questions
No questions were found.