github.com/cloudwego/hertz@v0.9.3/pkg/common/bytebufferpool/pool.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package bytebufferpool 43 44 import ( 45 "sort" 46 "sync" 47 "sync/atomic" 48 ) 49 50 const ( 51 minBitSize = 6 // 2**6=64 is a CPU cache line size 52 steps = 20 53 54 minSize = 1 << minBitSize 55 maxSize = 1 << (minBitSize + steps - 1) 56 57 calibrateCallsThreshold = 42000 58 maxPercentile = 0.95 59 ) 60 61 // Pool represents byte buffer pool. 62 // 63 // Distinct pools may be used for distinct types of byte buffers. 64 // Properly determined byte buffer types with their own pools may help reducing 65 // memory waste. 66 type Pool struct { 67 calls [steps]uint64 68 calibrating uint64 69 70 defaultSize uint64 71 maxSize uint64 72 73 pool sync.Pool 74 } 75 76 var defaultPool Pool 77 78 // Get returns an empty byte buffer from the pool. 79 // 80 // Got byte buffer may be returned to the pool via Put call. 81 // This reduces the number of memory allocations required for byte buffer 82 // management. 83 func Get() *ByteBuffer { return defaultPool.Get() } 84 85 // Get returns new byte buffer with zero length. 86 // 87 // The byte buffer may be returned to the pool via Put after the use 88 // in order to minimize GC overhead. 89 func (p *Pool) Get() *ByteBuffer { 90 v := p.pool.Get() 91 if v != nil { 92 return v.(*ByteBuffer) 93 } 94 return &ByteBuffer{ 95 B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)), 96 } 97 } 98 99 // Put returns byte buffer to the pool. 100 // 101 // ByteBuffer.B mustn't be touched after returning it to the pool. 102 // Otherwise data races will occur. 103 func Put(b *ByteBuffer) { defaultPool.Put(b) } 104 105 // Put releases byte buffer obtained via Get to the pool. 106 // 107 // The buffer mustn't be accessed after returning to the pool. 108 func (p *Pool) Put(b *ByteBuffer) { 109 idx := index(len(b.B)) 110 111 if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold { 112 p.calibrate() 113 } 114 115 maxSize := int(atomic.LoadUint64(&p.maxSize)) 116 if maxSize == 0 || cap(b.B) <= maxSize { 117 b.Reset() 118 p.pool.Put(b) 119 } 120 } 121 122 func (p *Pool) calibrate() { 123 if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) { 124 return 125 } 126 127 a := make(callSizes, 0, steps) 128 var callsSum uint64 129 for i := uint64(0); i < steps; i++ { 130 calls := atomic.SwapUint64(&p.calls[i], 0) 131 callsSum += calls 132 a = append(a, callSize{ 133 calls: calls, 134 size: minSize << i, 135 }) 136 } 137 sort.Sort(a) 138 139 defaultSize := a[0].size 140 maxSize := defaultSize 141 142 maxSum := uint64(float64(callsSum) * maxPercentile) 143 callsSum = 0 144 for i := 0; i < steps; i++ { 145 if callsSum > maxSum { 146 break 147 } 148 callsSum += a[i].calls 149 size := a[i].size 150 if size > maxSize { 151 maxSize = size 152 } 153 } 154 155 atomic.StoreUint64(&p.defaultSize, defaultSize) 156 atomic.StoreUint64(&p.maxSize, maxSize) 157 158 atomic.StoreUint64(&p.calibrating, 0) 159 } 160 161 type callSize struct { 162 calls uint64 163 size uint64 164 } 165 166 type callSizes []callSize 167 168 func (ci callSizes) Len() int { 169 return len(ci) 170 } 171 172 func (ci callSizes) Less(i, j int) bool { 173 return ci[i].calls > ci[j].calls 174 } 175 176 func (ci callSizes) Swap(i, j int) { 177 ci[i], ci[j] = ci[j], ci[i] 178 } 179 180 func index(n int) int { 181 n-- 182 n >>= minBitSize 183 idx := 0 184 for n > 0 { 185 n >>= 1 186 idx++ 187 } 188 if idx >= steps { 189 idx = steps - 1 190 } 191 return idx 192 }