Skip to content

Commit 1a1367c

Browse files
pohlydims
authored andcommitted
buffer: use sync.Pool
This simplifies the code. Instead of different instances, the package now maintains a global pool. This makes the text logger struct a bit smaller and thus cheaper to copy in the With* functions. Performance is about the same as before: name old time/op new time/op delta Header-36 1.68µs ± 7% 1.62µs ± 6% ~ (p=0.246 n=5+5) HeaderWithDir-36 1.63µs ± 6% 1.59µs ± 6% ~ (p=0.690 n=5+5) name old alloc/op new alloc/op delta Header-36 216B ± 0% 216B ± 0% ~ (all equal) HeaderWithDir-36 216B ± 0% 216B ± 0% ~ (all equal) name old allocs/op new allocs/op delta Header-36 2.00 ± 0% 2.00 ± 0% ~ (all equal) HeaderWithDir-36 2.00 ± 0% 2.00 ± 0% ~ (all equal) The text logger didn't actually return the buffer. Now it does.
1 parent ff4f80f commit 1a1367c

File tree

4 files changed

+32
-60
lines changed

4 files changed

+32
-60
lines changed

internal/buffer/buffer.go

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,44 +40,22 @@ type Buffer struct {
4040
next *Buffer
4141
}
4242

43-
// Buffers manages the reuse of individual buffer instances. It is thread-safe.
44-
type Buffers struct {
45-
// mu protects the free list. It is separate from the main mutex
46-
// so buffers can be grabbed and printed to without holding the main lock,
47-
// for better parallelization.
48-
mu sync.Mutex
49-
50-
// freeList is a list of byte buffers, maintained under mu.
51-
freeList *Buffer
43+
var buffers = sync.Pool{
44+
New: func() interface{} {
45+
return new(Buffer)
46+
},
5247
}
5348

5449
// GetBuffer returns a new, ready-to-use buffer.
55-
func (bl *Buffers) GetBuffer() *Buffer {
56-
bl.mu.Lock()
57-
b := bl.freeList
58-
if b != nil {
59-
bl.freeList = b.next
60-
}
61-
bl.mu.Unlock()
62-
if b == nil {
63-
b = new(Buffer)
64-
} else {
65-
b.next = nil
66-
b.Reset()
67-
}
50+
func GetBuffer() *Buffer {
51+
b := buffers.Get().(*Buffer)
52+
b.Reset()
6853
return b
6954
}
7055

7156
// PutBuffer returns a buffer to the free list.
72-
func (bl *Buffers) PutBuffer(b *Buffer) {
73-
if b.Len() >= 256 {
74-
// Let big buffers die a natural death.
75-
return
76-
}
77-
bl.mu.Lock()
78-
b.next = bl.freeList
79-
bl.freeList = b
80-
bl.mu.Unlock()
57+
func PutBuffer(b *Buffer) {
58+
buffers.Put(b)
8159
}
8260

8361
// Some custom tiny helper functions to print the log header efficiently.

klog.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -532,11 +532,6 @@ func (s settings) deepCopy() settings {
532532
type loggingT struct {
533533
settings
534534

535-
// bufferCache maintains the free list. It uses its own mutex
536-
// so buffers can be grabbed and printed to without holding the main lock,
537-
// for better parallelization.
538-
bufferCache buffer.Buffers
539-
540535
// flushD holds a flushDaemon that frequently flushes log file buffers.
541536
// Uses its own mutex.
542537
flushD *flushDaemon
@@ -664,7 +659,7 @@ func (l *loggingT) header(s severity.Severity, depth int) (*buffer.Buffer, strin
664659

665660
// formatHeader formats a log header using the provided file name and line number.
666661
func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buffer.Buffer {
667-
buf := l.bufferCache.GetBuffer()
662+
buf := buffer.GetBuffer()
668663
if l.skipHeaders {
669664
return buf
670665
}
@@ -682,8 +677,8 @@ func (l *loggingT) printlnDepth(s severity.Severity, logger *logr.Logger, filter
682677
// if logger is set, we clear the generated header as we rely on the backing
683678
// logger implementation to print headers
684679
if logger != nil {
685-
l.bufferCache.PutBuffer(buf)
686-
buf = l.bufferCache.GetBuffer()
680+
buffer.PutBuffer(buf)
681+
buf = buffer.GetBuffer()
687682
}
688683
if filter != nil {
689684
args = filter.Filter(args)
@@ -701,8 +696,8 @@ func (l *loggingT) printDepth(s severity.Severity, logger *logr.Logger, filter L
701696
// if logr is set, we clear the generated header as we rely on the backing
702697
// logr implementation to print headers
703698
if logger != nil {
704-
l.bufferCache.PutBuffer(buf)
705-
buf = l.bufferCache.GetBuffer()
699+
buffer.PutBuffer(buf)
700+
buf = buffer.GetBuffer()
706701
}
707702
if filter != nil {
708703
args = filter.Filter(args)
@@ -723,8 +718,8 @@ func (l *loggingT) printfDepth(s severity.Severity, logger *logr.Logger, filter
723718
// if logr is set, we clear the generated header as we rely on the backing
724719
// logr implementation to print headers
725720
if logger != nil {
726-
l.bufferCache.PutBuffer(buf)
727-
buf = l.bufferCache.GetBuffer()
721+
buffer.PutBuffer(buf)
722+
buf = buffer.GetBuffer()
728723
}
729724
if filter != nil {
730725
format, args = filter.FilterF(format, args)
@@ -744,8 +739,8 @@ func (l *loggingT) printWithFileLine(s severity.Severity, logger *logr.Logger, f
744739
// if logr is set, we clear the generated header as we rely on the backing
745740
// logr implementation to print headers
746741
if logger != nil {
747-
l.bufferCache.PutBuffer(buf)
748-
buf = l.bufferCache.GetBuffer()
742+
buffer.PutBuffer(buf)
743+
buf = buffer.GetBuffer()
749744
}
750745
if filter != nil {
751746
args = filter.Filter(args)
@@ -785,7 +780,7 @@ func (l *loggingT) infoS(logger *logr.Logger, filter LogFilter, depth int, msg s
785780
// set log severity by s
786781
func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string, keysAndValues ...interface{}) {
787782
// Only create a new buffer if we don't have one cached.
788-
b := l.bufferCache.GetBuffer()
783+
b := buffer.GetBuffer()
789784
// The message is always quoted, even if it contains line breaks.
790785
// If developers want multi-line output, they should use a small, fixed
791786
// message and put the multi-line output into a value.
@@ -796,7 +791,7 @@ func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string,
796791
serialize.KVListFormat(&b.Buffer, keysAndValues...)
797792
l.printDepth(s, logging.logger, nil, depth+1, &b.Buffer)
798793
// Make the buffer available for reuse.
799-
l.bufferCache.PutBuffer(b)
794+
buffer.PutBuffer(b)
800795
}
801796

802797
// redirectBuffer is used to set an alternate destination for the logs
@@ -948,7 +943,7 @@ func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buf
948943
timeoutFlush(ExitFlushTimeout)
949944
OsExit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
950945
}
951-
l.bufferCache.PutBuffer(buf)
946+
buffer.PutBuffer(buf)
952947

953948
if stats := severityStats[s]; stats != nil {
954949
atomic.AddInt64(&stats.lines, 1)

klog_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,15 +623,15 @@ func TestLogBacktraceAt(t *testing.T) {
623623
func BenchmarkHeader(b *testing.B) {
624624
for i := 0; i < b.N; i++ {
625625
buf, _, _ := logging.header(severity.InfoLog, 0)
626-
logging.bufferCache.PutBuffer(buf)
626+
buffer.PutBuffer(buf)
627627
}
628628
}
629629

630630
func BenchmarkHeaderWithDir(b *testing.B) {
631631
logging.addDirHeader = true
632632
for i := 0; i < b.N; i++ {
633633
buf, _, _ := logging.header(severity.InfoLog, 0)
634-
logging.bufferCache.PutBuffer(buf)
634+
buffer.PutBuffer(buf)
635635
}
636636
}
637637

textlogger/textlogger.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,17 @@ var (
5656
// later release. The behavior of the returned Logger may change.
5757
func NewLogger(c *Config) logr.Logger {
5858
return logr.New(&tlogger{
59-
prefix: "",
60-
values: nil,
61-
config: c,
62-
bufferCache: &buffer.Buffers{},
59+
prefix: "",
60+
values: nil,
61+
config: c,
6362
})
6463
}
6564

6665
type tlogger struct {
67-
callDepth int
68-
prefix string
69-
values []interface{}
70-
config *Config
71-
bufferCache *buffer.Buffers
66+
callDepth int
67+
prefix string
68+
values []interface{}
69+
config *Config
7270
}
7371

7472
func copySlice(in []interface{}) []interface{} {
@@ -103,7 +101,8 @@ func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
103101

104102
func (l *tlogger) print(err error, s severity.Severity, msg string, kvList []interface{}) {
105103
// Only create a new buffer if we don't have one cached.
106-
b := l.bufferCache.GetBuffer()
104+
b := buffer.GetBuffer()
105+
defer buffer.PutBuffer(b)
107106

108107
// Determine caller.
109108
// +1 for this frame, +1 for Info/Error.

0 commit comments

Comments
 (0)