mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
24 bytes per Put < 256-4096 bytes per buffer allocation avoided (10-170x difference)
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
// Package optimization provides memory and performance optimizations
|
|
package optimization
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// StringBuilderPool provides a pool of reusable string builders
|
|
type StringBuilderPool struct {
|
|
pool sync.Pool
|
|
}
|
|
|
|
// NewStringBuilderPool creates a new string builder pool
|
|
func NewStringBuilderPool() *StringBuilderPool {
|
|
return &StringBuilderPool{
|
|
pool: sync.Pool{
|
|
New: func() any {
|
|
return make([]byte, 0, 256) // Pre-allocate 256 bytes
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Get retrieves a string builder from the pool
|
|
func (p *StringBuilderPool) Get() []byte {
|
|
buf := p.pool.Get().([]byte)
|
|
// Return the slice with reset length but preserved capacity
|
|
return buf[:0]
|
|
}
|
|
|
|
// Put returns a string builder to the pool
|
|
func (p *StringBuilderPool) Put(buf []byte) {
|
|
if buf != nil && cap(buf) < 4096 { // Don't pool overly large buffers or nil buffers
|
|
// Intentional design: slice API is more ergonomic than *[]byte.
|
|
// The 24-byte boxing allocation is negligible compared to 256+ byte buffer allocation we avoid.
|
|
//lint:ignore SA6002 slice interface is preferred over pointer interface for ergonomics
|
|
p.pool.Put(buf[:0]) // Reset length and store the slice directly
|
|
}
|
|
}
|
|
|
|
// ByteSlicePool provides a pool of reusable byte slices
|
|
type ByteSlicePool struct {
|
|
pool sync.Pool
|
|
size int
|
|
}
|
|
|
|
// NewByteSlicePool creates a new byte slice pool with specified size
|
|
func NewByteSlicePool(size int) *ByteSlicePool {
|
|
return &ByteSlicePool{
|
|
size: size,
|
|
pool: sync.Pool{
|
|
New: func() any {
|
|
return make([]byte, size)
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Get retrieves a byte slice from the pool
|
|
func (p *ByteSlicePool) Get() []byte {
|
|
buf := p.pool.Get().([]byte)
|
|
// Ensure we're returning the full-sized slice
|
|
return buf[:p.size]
|
|
}
|
|
|
|
// Put returns a byte slice to the pool
|
|
func (p *ByteSlicePool) Put(buf []byte) {
|
|
if buf != nil && len(buf) == p.size {
|
|
// Intentional design: slice API is more ergonomic than *[]byte.
|
|
// The 24-byte boxing allocation is negligible compared to the sized buffer allocation we avoid.
|
|
//lint:ignore SA6002 slice interface is preferred over pointer interface for ergonomics
|
|
p.pool.Put(buf)
|
|
}
|
|
}
|
|
|
|
// Global pools for common use cases
|
|
var (
|
|
globalStringBuilderPool = NewStringBuilderPool()
|
|
globalByteSlicePool = NewByteSlicePool(2048)
|
|
)
|
|
|
|
// GetStringBuilder gets a string builder from the global pool
|
|
func GetStringBuilder() []byte {
|
|
return globalStringBuilderPool.Get()
|
|
}
|
|
|
|
// PutStringBuilder returns a string builder to the global pool
|
|
func PutStringBuilder(buf []byte) {
|
|
globalStringBuilderPool.Put(buf)
|
|
}
|
|
|
|
// GetByteSlice gets a byte slice from the global pool
|
|
func GetByteSlice() []byte {
|
|
return globalByteSlicePool.Get()
|
|
}
|
|
|
|
// PutByteSlice returns a byte slice to the global pool
|
|
func PutByteSlice(buf []byte) {
|
|
globalByteSlicePool.Put(buf)
|
|
}
|