Skip to content

Commit 50c6321

Browse files
authored
mem: Allocate at 4KiB boundaries in the fallback buffer pool. (#8705)
By rounding up to the nearest page, we avoid repeatedly allocating similar sizes if requests happen to arrive in roughly increasing order. The GCS client sends messages with 2MiB of data repeatedly when writing a large object. Therefore it has to repeatedly allocate just over 2MiB. This ultimately results in many, many allocations in the fallback buffer pool. In practice rounding up yields at least a 10x reduction in RAM when running 100 concurrent large writes. This is probably not unique to GCS: anyone who sends large messages may be affected. This change in simpleBufferPool seems worthwhile vs. adding a tier. We use simpleBufferPool for any size greater than 1MiB, so this effectively lets us discover a reasonably tight tier around any large message size that comes in frequently. It increases infrequent allocation sizes by no more than 0.4%. RELEASE NOTES: * mem: round up to nearest 4KiB for pool allocations larger than 1MiB
1 parent 1ed87ec commit 50c6321

File tree

1 file changed

+11
-2
lines changed

1 file changed

+11
-2
lines changed

mem/buffer_pool.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ type BufferPool interface {
3838
Put(*[]byte)
3939
}
4040

41+
const goPageSize = 4 << 10 // 4KiB. N.B. this must be a power of 2.
42+
4143
var defaultBufferPoolSizes = []int{
4244
256,
43-
4 << 10, // 4KB (go page size)
45+
goPageSize,
4446
16 << 10, // 16KB (max HTTP/2 frame size used by gRPC)
4547
32 << 10, // 32KB (default buffer size for io.Copy)
4648
1 << 20, // 1MB
@@ -172,7 +174,14 @@ func (p *simpleBufferPool) Get(size int) *[]byte {
172174
p.pool.Put(bs)
173175
}
174176

175-
b := make([]byte, size)
177+
// If we're going to allocate, round up to the nearest page. This way if
178+
// requests frequently arrive with small variation we don't allocate
179+
// repeatedly if we get unlucky and they increase over time. By default we
180+
// only allocate here if size > 1MiB. Because goPageSize is a power of 2, we
181+
// can round up efficiently.
182+
allocSize := (size + goPageSize - 1) & ^(goPageSize - 1)
183+
184+
b := make([]byte, size, allocSize)
176185
return &b
177186
}
178187

0 commit comments

Comments
 (0)