source: code/trunk/vendor/github.com/valyala/bytebufferpool/pool.go@ 145

Last change on this file since 145 was 145, checked in by Izuru Yakumo, 22 months ago

Updated the Makefile and vendored depedencies

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 2.9 KB
Line 
1package bytebufferpool
2
3import (
4 "sort"
5 "sync"
6 "sync/atomic"
7)
8
9const (
10 minBitSize = 6 // 2**6=64 is a CPU cache line size
11 steps = 20
12
13 minSize = 1 << minBitSize
14 maxSize = 1 << (minBitSize + steps - 1)
15
16 calibrateCallsThreshold = 42000
17 maxPercentile = 0.95
18)
19
20// Pool represents byte buffer pool.
21//
22// Distinct pools may be used for distinct types of byte buffers.
23// Properly determined byte buffer types with their own pools may help reducing
24// memory waste.
25type Pool struct {
26 calls [steps]uint64
27 calibrating uint64
28
29 defaultSize uint64
30 maxSize uint64
31
32 pool sync.Pool
33}
34
35var defaultPool Pool
36
37// Get returns an empty byte buffer from the pool.
38//
39// Got byte buffer may be returned to the pool via Put call.
40// This reduces the number of memory allocations required for byte buffer
41// management.
42func Get() *ByteBuffer { return defaultPool.Get() }
43
44// Get returns new byte buffer with zero length.
45//
46// The byte buffer may be returned to the pool via Put after the use
47// in order to minimize GC overhead.
48func (p *Pool) Get() *ByteBuffer {
49 v := p.pool.Get()
50 if v != nil {
51 return v.(*ByteBuffer)
52 }
53 return &ByteBuffer{
54 B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)),
55 }
56}
57
58// Put returns byte buffer to the pool.
59//
60// ByteBuffer.B mustn't be touched after returning it to the pool.
61// Otherwise data races will occur.
62func Put(b *ByteBuffer) { defaultPool.Put(b) }
63
64// Put releases byte buffer obtained via Get to the pool.
65//
66// The buffer mustn't be accessed after returning to the pool.
67func (p *Pool) Put(b *ByteBuffer) {
68 idx := index(len(b.B))
69
70 if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold {
71 p.calibrate()
72 }
73
74 maxSize := int(atomic.LoadUint64(&p.maxSize))
75 if maxSize == 0 || cap(b.B) <= maxSize {
76 b.Reset()
77 p.pool.Put(b)
78 }
79}
80
81func (p *Pool) calibrate() {
82 if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) {
83 return
84 }
85
86 a := make(callSizes, 0, steps)
87 var callsSum uint64
88 for i := uint64(0); i < steps; i++ {
89 calls := atomic.SwapUint64(&p.calls[i], 0)
90 callsSum += calls
91 a = append(a, callSize{
92 calls: calls,
93 size: minSize << i,
94 })
95 }
96 sort.Sort(a)
97
98 defaultSize := a[0].size
99 maxSize := defaultSize
100
101 maxSum := uint64(float64(callsSum) * maxPercentile)
102 callsSum = 0
103 for i := 0; i < steps; i++ {
104 if callsSum > maxSum {
105 break
106 }
107 callsSum += a[i].calls
108 size := a[i].size
109 if size > maxSize {
110 maxSize = size
111 }
112 }
113
114 atomic.StoreUint64(&p.defaultSize, defaultSize)
115 atomic.StoreUint64(&p.maxSize, maxSize)
116
117 atomic.StoreUint64(&p.calibrating, 0)
118}
119
120type callSize struct {
121 calls uint64
122 size uint64
123}
124
125type callSizes []callSize
126
127func (ci callSizes) Len() int {
128 return len(ci)
129}
130
131func (ci callSizes) Less(i, j int) bool {
132 return ci[i].calls > ci[j].calls
133}
134
135func (ci callSizes) Swap(i, j int) {
136 ci[i], ci[j] = ci[j], ci[i]
137}
138
139func index(n int) int {
140 n--
141 n >>= minBitSize
142 idx := 0
143 for n > 0 {
144 n >>= 1
145 idx++
146 }
147 if idx >= steps {
148 idx = steps - 1
149 }
150 return idx
151}
Note: See TracBrowser for help on using the repository browser.