Buffer Pool¶
The Buffer Pool module provides memory-efficient buffer management for FlashFS operations, particularly for file hashing and I/O operations. It reduces memory allocations and garbage collection pressure by reusing byte slices.
Overview¶
The Buffer Pool is designed to:
- Provide a pool of reusable byte slices
- Reduce memory allocations and garbage collection overhead
- Optimize performance for I/O-intensive operations
- Track usage metrics for monitoring and tuning
- Support configurable buffer sizes and pool behavior
Key Components¶
BufferPool¶
The core component is the BufferPool
struct, which manages a pool of byte slices:
type BufferPool struct {
pool sync.Pool
size int
name string
metrics *BufferPoolMetrics
minSize int
maxSize int
shortageNotification chan struct{}
getTimeout time.Duration
}
BufferPoolOptions¶
The behavior of a buffer pool can be configured using the BufferPoolOptions
struct:
type BufferPoolOptions struct {
BufferSize int // Initial and default buffer size
MinBufferSize int // Minimum buffer size to accept back into the pool
MaxBufferSize int // Maximum buffer size to create
PoolName string // Name for the pool (for metrics/logging)
ShortageNotification chan struct{} // Channel for communicating buffer supply shortages
GetTimeout time.Duration // Timeout for waiting on a buffer
}
BufferPoolMetrics¶
The BufferPoolMetrics
struct tracks usage statistics for a buffer pool:
type BufferPoolMetrics struct {
Gets uint64
Puts uint64
NewAllocations uint64
ResizeAllocations uint64
RejectedBuffers uint64
Size int
Timeouts uint64
Name string
}
Core Functions¶
Creating and Using a Buffer Pool¶
// Create a new buffer pool with custom options
options := hash.DefaultBufferPoolOptions()
options.BufferSize = 32 * 1024 * 1024 // 32MB buffers
options.PoolName = "large-file-pool"
pool := hash.NewBufferPool(options)
// Get a buffer from the pool
buffer, err := pool.Get()
if err != nil {
// Handle error
}
// Use the buffer for I/O operations
// ...
// Return the buffer to the pool when done
pool.Put(buffer)
Using the Default Buffer Pool¶
FlashFS provides a default buffer pool for convenience:
// Get a buffer from the default pool
buffer, err := hash.GetBuffer()
if err != nil {
// Handle error
}
// Use the buffer
// ...
// Return the buffer to the default pool
hash.PutBuffer(buffer)
Buffer Management¶
The buffer pool implements several strategies to efficiently manage memory:
- Buffer Reuse: Buffers are returned to the pool after use and reused for subsequent operations
- Size Management: Buffers that are too small are rejected, while oversized buffers are trimmed
- Timeout Handling: Optional timeouts prevent blocking when buffers are scarce
- Shortage Notification: Optional channel notifications when buffer demand exceeds supply
Performance Considerations¶
- Buffer Size: Larger buffers improve throughput for sequential I/O but consume more memory
- Pool Size: The pool automatically grows and shrinks based on demand
- Concurrency: The buffer pool is thread-safe and can be used from multiple goroutines
- Memory Pressure: Monitor metrics to detect memory pressure and adjust buffer sizes accordingly
Metrics and Monitoring¶
The buffer pool provides detailed metrics to help monitor and tune performance:
// Get metrics from a buffer pool
metrics := pool.Metrics()
fmt.Println(metrics.String())
// Output:
// BufferPool 'large-file-pool' Metrics:
// Gets: 1250
// Puts: 1248
// New Allocations: 10
// Resize Allocations: 2
// Rejected Buffers: 5
// Size: 33554432
// Timeouts: 0
Example Usage¶
// Create a custom buffer pool for large file operations
largeFilePool := hash.NewBufferPool(hash.BufferPoolOptions{
BufferSize: 64 * 1024 * 1024, // 64MB
MinBufferSize: 32 * 1024 * 1024, // 32MB
MaxBufferSize: 128 * 1024 * 1024, // 128MB
PoolName: "large-file-pool",
GetTimeout: 5 * time.Second,
})
// Process multiple files using the pool
for _, filePath := range filePaths {
// Get a buffer from the pool
buffer, err := largeFilePool.Get()
if err != nil {
log.Printf("Failed to get buffer: %v", err)
continue
}
// Use the buffer for file processing
file, err := os.Open(filePath)
if err != nil {
largeFilePool.Put(buffer) // Return buffer to pool
log.Printf("Failed to open file: %v", err)
continue
}
// Process the file using the buffer
// ...
file.Close()
// Return the buffer to the pool
largeFilePool.Put(buffer)
}
// Print metrics
fmt.Println(largeFilePool.Metrics().String())
Integration with Hash Module¶
The Buffer Pool is tightly integrated with the Hash module to optimize memory usage during hashing operations:
// The hash.File function automatically uses the buffer pool
result := hash.File(path, hash.DefaultOptions())
// The hash.Reader function also uses the buffer pool
result := hash.Reader(reader, hash.BLAKE3)