1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2024-12-09 09:56:01 +02:00
imgproxy/bufpool.go

158 lines
2.8 KiB
Go
Raw Normal View History

package main
import (
"bytes"
"runtime"
2019-01-30 10:36:19 +02:00
"sort"
"sync"
)
2019-01-30 10:36:19 +02:00
type intSlice []int
func (p intSlice) Len() int { return len(p) }
func (p intSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p intSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type bufPool struct {
name string
defaultSize int
maxSize int
buffers []*bytes.Buffer
2019-01-30 10:36:19 +02:00
calls intSlice
callInd int
mutex sync.Mutex
}
func newBufPool(name string, n int, defaultSize int) *bufPool {
2019-01-30 10:36:19 +02:00
pool := bufPool{
name: name,
defaultSize: defaultSize,
buffers: make([]*bytes.Buffer, n),
calls: make(intSlice, conf.BufferPoolCalibrationThreshold),
2019-01-30 10:36:19 +02:00
}
for i := range pool.buffers {
pool.buffers[i] = new(bytes.Buffer)
}
return &pool
}
2019-01-30 10:36:19 +02:00
func (p *bufPool) calibrateAndClean() {
sort.Sort(p.calls)
2019-01-28 18:18:54 +02:00
2019-01-30 12:31:00 +02:00
pos := int(float64(len(p.calls)) * 0.95)
score := p.calls[pos]
2019-01-30 10:36:19 +02:00
2019-01-30 12:31:00 +02:00
p.callInd = 0
2019-03-11 10:30:00 +02:00
p.maxSize = p.normalizeSize(score)
2019-01-30 10:36:19 +02:00
p.defaultSize = maxInt(p.defaultSize, p.calls[0])
p.maxSize = maxInt(p.defaultSize, p.maxSize)
cleaned := false
2019-01-30 12:31:00 +02:00
for i, buf := range p.buffers {
if buf != nil && buf.Cap() > p.maxSize {
2019-01-30 12:31:00 +02:00
p.buffers[i] = nil
cleaned = true
2019-01-30 12:31:00 +02:00
}
}
if cleaned {
runtime.GC()
}
if prometheusEnabled {
setPrometheusBufferDefaultSize(p.name, p.defaultSize)
setPrometheusBufferMaxSize(p.name, p.maxSize)
}
}
2019-01-30 12:31:00 +02:00
func (p *bufPool) Get(size int) *bytes.Buffer {
p.mutex.Lock()
defer p.mutex.Unlock()
2019-03-11 10:30:00 +02:00
size = p.normalizeSize(size)
2019-01-30 10:36:19 +02:00
minSize, maxSize, minInd, maxInd := -1, -1, -1, -1
for i := 0; i < len(p.buffers); i++ {
if p.buffers[i] != nil {
cap := p.buffers[i].Cap()
if size > 0 && cap >= size && (minSize > cap || minSize == -1) {
minSize = cap
minInd = i
}
if cap > maxSize {
maxSize = cap
maxInd = i
}
}
}
2019-01-30 10:36:19 +02:00
var buf *bytes.Buffer
switch {
case minInd >= 0:
2019-01-30 10:36:19 +02:00
// We found buffer with the desired size
buf = p.buffers[minInd]
p.buffers[minInd] = nil
case maxInd >= 0:
2019-01-30 10:36:19 +02:00
// We didn't find buffer with the desired size
buf = p.buffers[maxInd]
p.buffers[maxInd] = nil
default:
2019-01-30 10:36:19 +02:00
// We didn't find buffers at all
buf = new(bytes.Buffer)
2019-01-30 10:36:19 +02:00
}
buf.Reset()
growSize := maxInt(size, p.defaultSize)
if growSize > buf.Cap() {
buf.Grow(growSize)
2019-01-30 13:45:31 +02:00
}
return buf
}
2019-01-30 12:31:00 +02:00
func (p *bufPool) Put(buf *bytes.Buffer) {
p.mutex.Lock()
defer p.mutex.Unlock()
2019-01-30 12:31:00 +02:00
if buf.Len() > 0 {
p.calls[p.callInd] = buf.Len()
p.callInd++
2019-01-30 10:36:19 +02:00
2019-01-30 12:31:00 +02:00
if p.callInd == len(p.calls) {
p.calibrateAndClean()
}
2019-01-30 10:36:19 +02:00
}
if p.maxSize > 0 && buf.Cap() > p.maxSize {
2019-01-30 10:36:19 +02:00
return
}
for i, b := range p.buffers {
if b == nil {
p.buffers[i] = buf
2019-01-30 12:31:00 +02:00
if prometheusEnabled && buf.Cap() > 0 {
observePrometheusBufferSize(p.name, buf.Cap())
2019-01-30 10:36:19 +02:00
}
2019-01-28 18:18:54 +02:00
2019-01-30 10:36:19 +02:00
return
}
2019-01-28 18:18:54 +02:00
}
}
2019-03-11 10:30:00 +02:00
func (p *bufPool) normalizeSize(n int) int {
return (n/bytes.MinRead + 2) * bytes.MinRead
}