-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-sugared logger always slower than the sugar counterpart #1385
Comments
Why did you close this @fracasula? |
Reopening as this does look like a real issue. I did a little bit of investigation, and it seems like a combination of sampling, and how the lifetime of the fields maps to the write. To make it easier to read the output, I'm changing the logger to write to cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{"/dev/null"}
l, err := cfg.Build() Otherwise, it's the same code as what you had, and it does show that sugar is faster, and we can see there's an additional alloc on the non-sugar logger,
If we disable sampling, the results change to what we'd expect, where sugar is slower, cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{"/dev/null"}
cfg.Sampling = nil
With sampling enabled, since the message is constant, most of the logs are dropped. When logs aren't dropped, the input To verify this, I put some temporary code into zap to pass in a copy of the fields to the core write, e.g., updating
With this copy in place, the escape analysis now sees that |
I needed more time to understand if I was reporting a false positive due to things like bad configuration. Compare these two benchmark for example. The first one with a custom configuration shows the non-sugared as much faster (like it should be) while the second one shows the opposite. The only thing that changes is the configuration but I did not have the time to investigate further and temporarily closed the issue. func BenchmarkZap(b *testing.B) {
newZap := func(lvl zapcore.Level) *zap.Logger {
ec := zap.NewProductionEncoderConfig()
ec.EncodeDuration = zapcore.NanosDurationEncoder
ec.EncodeTime = zapcore.EpochNanosTimeEncoder
enc := zapcore.NewJSONEncoder(ec)
return zap.New(zapcore.NewCore(
enc,
&discarder{},
lvl,
))
}
b.Run("Zap", func(b *testing.B) {
logger := newZap(zapcore.DebugLevel)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.Debug("message", zap.String("key1", "111"), zap.Int("key2", 222))
}
})
})
b.Run("Zap.Sugar", func(b *testing.B) {
logger := newZap(zapcore.DebugLevel).Sugar()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.Debugw("message", "key1", "111", "key2", 222)
}
})
})
}
func BenchmarkZapSlow(b *testing.B) {
b.Run("Zap", func(b *testing.B) {
l, err := zap.NewProduction()
if err != nil {
b.Fatal(err)
}
defer l.Sync()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
l.Debug("message", zap.String("key1", "111"), zap.Int("key2", 222))
}
})
})
b.Run("Zap.Sugar", func(b *testing.B) {
l, err := zap.NewProduction()
if err != nil {
b.Fatal(err)
}
ls := l.Sugar()
defer ls.Sync()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
ls.Debugw("message", "key1", "111", "key2", 222)
}
})
})
} Gives:
Please note that:
|
Describe the bug
The non-sugared logger is always slower than the sugar counterpart.
Can someone explain how come I'm getting these results?
To Reproduce
I'm using the latest zap i.e. v1.26.0
When running the above benchmark with the
Debug
functions instead ofInfo
the situation is even worse:The
Debug
benchmark gives the following (beware that I did not change the log level so nothing gets actually printed on screen when running this benchmark):Expected behavior
Non-sugared logger should be faster.
As per documentation it's supposed to be used in performance critical areas.
Additional context
None.
The text was updated successfully, but these errors were encountered: