Skip to content

Commit 005f76f

Browse files
committed
buffer+log: revamp the buffer pool
Update the buffer pool to use a concrete type and steel some other updates from the buffer pool used by std lib. Also adds a few useful helpers that we will use later on and also factors out the timestamp writing part from formatHeader since we will use that again later.
1 parent c3b89c2 commit 005f76f

File tree

2 files changed

+82
-47
lines changed

2 files changed

+82
-47
lines changed

buffer.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Adapted from go/src/log/slog/internal/buffer.go
6+
7+
package btclog
8+
9+
import "sync"
10+
11+
type buffer []byte
12+
13+
// bufferPool defines a concurrent safe free list of byte slices used to provide
14+
// temporary buffers for formatting log messages prior to outputting them.
15+
var bufferPool = sync.Pool{
16+
New: func() any {
17+
b := make([]byte, 0, 1024)
18+
return (*buffer)(&b)
19+
},
20+
}
21+
22+
// newBuffer returns a byte slice from the free list. A new buffer is allocated
23+
// if there are not any available on the free list. The returned byte slice
24+
// should be returned to the fee list by using the recycleBuffer function when
25+
// the caller is done with it.
26+
func newBuffer() *buffer {
27+
return bufferPool.Get().(*buffer)
28+
}
29+
30+
// free puts the provided byte slice, which should have been obtained via the
31+
// newBuffer function, back on the free list.
32+
func (b *buffer) free() {
33+
// To reduce peak allocation, return only smaller buffers to the pool.
34+
const maxBufferSize = 16 << 10
35+
if cap(*b) <= maxBufferSize {
36+
*b = (*b)[:0]
37+
bufferPool.Put(b)
38+
}
39+
}
40+
41+
func (b *buffer) writeByte(p byte) {
42+
*b = append(*b, p)
43+
}
44+
45+
func (b *buffer) writeBytes(p []byte) {
46+
*b = append(*b, p...)
47+
}
48+
49+
func (b *buffer) writeString(s string) {
50+
*b = append(*b, s...)
51+
}

log.go

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -105,34 +105,10 @@ func WithFlags(flags uint32) BackendOption {
105105
}
106106
}
107107

108-
// bufferPool defines a concurrent safe free list of byte slices used to provide
109-
// temporary buffers for formatting log messages prior to outputting them.
110-
var bufferPool = sync.Pool{
111-
New: func() any {
112-
b := make([]byte, 0, 120)
113-
return &b // pointer to slice to avoid boxing alloc
114-
},
115-
}
116-
117-
// buffer returns a byte slice from the free list. A new buffer is allocated if
118-
// there are not any available on the free list. The returned byte slice should
119-
// be returned to the fee list by using the recycleBuffer function when the
120-
// caller is done with it.
121-
func buffer() *[]byte {
122-
return bufferPool.Get().(*[]byte)
123-
}
124-
125-
// recycleBuffer puts the provided byte slice, which should have been obtain via
126-
// the buffer function, back on the free list.
127-
func recycleBuffer(b *[]byte) {
128-
*b = (*b)[:0]
129-
bufferPool.Put(b)
130-
}
131-
132108
// From stdlib log package.
133109
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid
134110
// zero-padding.
135-
func itoa(buf *[]byte, i int, wid int) {
111+
func itoa(buf *buffer, i int, wid int) {
136112
// Assemble decimal in reverse order.
137113
var b [20]byte
138114
bp := len(b) - 1
@@ -145,30 +121,15 @@ func itoa(buf *[]byte, i int, wid int) {
145121
}
146122
// i < 10
147123
b[bp] = byte('0' + i)
148-
*buf = append(*buf, b[bp:]...)
124+
buf.writeBytes(b[bp:])
149125
}
150126

151127
// Appends a header in the default format 'YYYY-MM-DD hh:mm:ss.sss [LVL] TAG: '.
152128
// If either of the Lshortfile or Llongfile flags are specified, the file named
153129
// and line number are included after the tag and before the final colon.
154-
func formatHeader(buf *[]byte, t time.Time, lvl, tag string, file string, line int) {
155-
year, month, day := t.Date()
156-
hour, min, sec := t.Clock()
157-
ms := t.Nanosecond() / 1e6
130+
func formatHeader(buf *buffer, t time.Time, lvl, tag string, file string, line int) {
131+
writeTimestamp(buf, t)
158132

159-
itoa(buf, year, 4)
160-
*buf = append(*buf, '-')
161-
itoa(buf, int(month), 2)
162-
*buf = append(*buf, '-')
163-
itoa(buf, day, 2)
164-
*buf = append(*buf, ' ')
165-
itoa(buf, hour, 2)
166-
*buf = append(*buf, ':')
167-
itoa(buf, min, 2)
168-
*buf = append(*buf, ':')
169-
itoa(buf, sec, 2)
170-
*buf = append(*buf, '.')
171-
itoa(buf, ms, 3)
172133
*buf = append(*buf, " ["...)
173134
*buf = append(*buf, lvl...)
174135
*buf = append(*buf, "] "...)
@@ -182,6 +143,29 @@ func formatHeader(buf *[]byte, t time.Time, lvl, tag string, file string, line i
182143
*buf = append(*buf, ": "...)
183144
}
184145

146+
// writeTimestamp writes the date in the format 'YYYY-MM-DD hh:mm:ss.sss' to the
147+
// buffer.
148+
func writeTimestamp(buf *buffer, t time.Time) {
149+
year, month, day := t.Date()
150+
hour, min, sec := t.Clock()
151+
ms := t.Nanosecond() / 1e6
152+
153+
itoa(buf, year, 4)
154+
buf.writeByte('-')
155+
itoa(buf, int(month), 2)
156+
buf.writeByte('-')
157+
itoa(buf, day, 2)
158+
buf.writeByte(' ')
159+
itoa(buf, hour, 2)
160+
buf.writeByte(':')
161+
itoa(buf, min, 2)
162+
buf.writeByte(':')
163+
itoa(buf, sec, 2)
164+
buf.writeByte('.')
165+
itoa(buf, ms, 3)
166+
buf.writeByte(' ')
167+
}
168+
185169
// calldepth is the call depth of the callsite function relative to the
186170
// caller of the subsystem logger. It is used to recover the filename and line
187171
// number of the logging call if either the short or long file flags are
@@ -215,7 +199,7 @@ func callsite(flag uint32) (string, int) {
215199
func (b *Backend) print(lvl, tag string, args ...any) {
216200
t := time.Now() // get as early as possible
217201

218-
bytebuf := buffer()
202+
bytebuf := newBuffer()
219203

220204
var file string
221205
var line int
@@ -232,7 +216,7 @@ func (b *Backend) print(lvl, tag string, args ...any) {
232216
b.w.Write(*bytebuf)
233217
b.mu.Unlock()
234218

235-
recycleBuffer(bytebuf)
219+
bytebuf.free()
236220
}
237221

238222
// printf outputs a log message to the writer associated with the backend after
@@ -242,7 +226,7 @@ func (b *Backend) print(lvl, tag string, args ...any) {
242226
func (b *Backend) printf(lvl, tag string, format string, args ...any) {
243227
t := time.Now() // get as early as possible
244228

245-
bytebuf := buffer()
229+
bytebuf := newBuffer()
246230

247231
var file string
248232
var line int
@@ -259,7 +243,7 @@ func (b *Backend) printf(lvl, tag string, format string, args ...any) {
259243
b.w.Write(*bytebuf)
260244
b.mu.Unlock()
261245

262-
recycleBuffer(bytebuf)
246+
bytebuf.free()
263247
}
264248

265249
// Logger returns a new logger for a particular subsystem that writes to the

0 commit comments

Comments
 (0)