github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/pool/object_test.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package pool 22 23 import ( 24 "runtime" 25 "strconv" 26 "testing" 27 "time" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 ) 32 33 func TestObjectPoolRefillOnLowWaterMark(t *testing.T) { 34 opts := NewObjectPoolOptions(). 35 SetSize(100). 36 SetRefillLowWatermark(0.25). 37 SetRefillHighWatermark(0.75) 38 39 pool := NewObjectPool(opts).(*objectPool) 40 pool.Init(func() interface{} { 41 return 1 42 }) 43 44 assert.Equal(t, 100, len(pool.values)) 45 46 for i := 0; i < 74; i++ { 47 pool.Get() 48 } 49 50 assert.Equal(t, 26, len(pool.values)) 51 52 // This should trigger a refill 53 pool.Get() 54 55 start := time.Now() 56 for time.Since(start) < 10*time.Second { 57 if len(pool.values) == 75 { 58 break 59 } 60 time.Sleep(time.Millisecond) 61 } 62 63 // Assert refilled 64 assert.Equal(t, 75, len(pool.values)) 65 } 66 67 func TestObjectPoolInitTwiceError(t *testing.T) { 68 var accessErr error 69 opts := NewObjectPoolOptions().SetOnPoolAccessErrorFn(func(err error) { 70 accessErr = err 71 }) 72 73 pool := NewObjectPool(opts) 74 pool.Init(func() interface{} { 75 return 1 76 }) 77 78 require.NoError(t, accessErr) 79 80 pool.Init(func() interface{} { 81 return 1 82 }) 83 84 assert.Error(t, accessErr) 85 assert.Equal(t, errPoolAlreadyInitialized, accessErr) 86 } 87 88 func TestObjectPoolGetBeforeInitError(t *testing.T) { 89 var accessErr error 90 opts := NewObjectPoolOptions().SetOnPoolAccessErrorFn(func(err error) { 91 accessErr = err 92 panic(err) 93 }) 94 95 pool := NewObjectPool(opts) 96 97 require.NoError(t, accessErr) 98 99 assert.Panics(t, func() { 100 pool.Get() 101 }) 102 103 assert.Error(t, accessErr) 104 assert.Equal(t, errPoolAccessBeforeInitialized, accessErr) 105 } 106 107 func TestObjectPoolPutBeforeInitError(t *testing.T) { 108 var accessErr error 109 opts := NewObjectPoolOptions().SetOnPoolAccessErrorFn(func(err error) { 110 accessErr = err 111 }) 112 113 pool := NewObjectPool(opts) 114 115 require.NoError(t, accessErr) 116 117 pool.Put(1) 118 119 assert.Error(t, accessErr) 120 assert.Equal(t, errPoolAccessBeforeInitialized, accessErr) 121 } 122 123 func BenchmarkObjectPoolGetPut(b *testing.B) { 124 opts := NewObjectPoolOptions().SetSize(1) 125 pool := NewObjectPool(opts) 126 pool.Init(func() interface{} { 127 b := make([]byte, 0, 16) 128 return &b 129 }) 130 131 b.ResetTimer() 132 for n := 0; n < b.N; n++ { 133 o := pool.Get().(*[]byte) 134 _ = *o 135 pool.Put(o) 136 } 137 } 138 139 // go test -benchmem -run=^$ github.com/m3db/m3/src/x/pool -bench '^(BenchmarkObjectPoolParallel)$' -cpu 1,2,4,6,8,12 140 func BenchmarkObjectPoolParallelGetPut(b *testing.B) { 141 type poolObj struct { 142 a, b int 143 c *int64 144 ts int64 145 } 146 147 p := NewObjectPool(NewObjectPoolOptions().SetSize(1024)) 148 p.Init(func() interface{} { 149 return &poolObj{} 150 }) 151 152 ts := time.Now().UnixNano() 153 154 b.ResetTimer() 155 b.RunParallel(func(pb *testing.PB) { 156 for pb.Next() { 157 op := p.Get() 158 obj, ok := op.(*poolObj) 159 if !ok { 160 b.Fail() 161 } 162 // do something with object, so there's something going on between gets/puts: 163 obj.a = b.N 164 obj.c = &ts 165 obj.ts = ts + int64(b.N) 166 p.Put(obj) 167 } 168 }) 169 } 170 171 func BenchmarkObjectPoolParallelGetMultiPutContended(b *testing.B) { 172 opts := NewObjectPoolOptions(). 173 SetSize(256) 174 175 p := NewObjectPool(opts) 176 p.Init(func() interface{} { 177 b := make([]byte, 0, 64) 178 return &b 179 }) 180 181 b.ResetTimer() 182 b.RunParallel(func(pb *testing.PB) { 183 for pb.Next() { 184 bufs := make([]*[]byte, 16) 185 for i := 0; i < len(bufs); i++ { 186 o, ok := p.Get().(*[]byte) 187 if !ok { 188 b.Fail() 189 } 190 bufs[i] = o 191 } 192 for i := 0; i < len(bufs); i++ { 193 o := bufs[i] 194 buf := *o 195 buf = strconv.AppendInt(buf[:0], 12344321, 10) 196 runtime.KeepAlive(buf) 197 p.Put(o) 198 } 199 } 200 }) 201 } 202 203 //nolint:dupl 204 func BenchmarkObjectPoolParallelGetMultiPutContendedDynamic(b *testing.B) { 205 opts := NewObjectPoolOptions(). 206 SetDynamic(true) 207 208 p := NewObjectPool(opts) 209 p.Init(func() interface{} { 210 b := make([]byte, 0, 64) 211 return &b 212 }) 213 214 b.ResetTimer() 215 b.RunParallel(func(pb *testing.PB) { 216 for pb.Next() { 217 bufs := make([]*[]byte, 16) 218 for i := 0; i < len(bufs); i++ { 219 o, ok := p.Get().(*[]byte) 220 if !ok { 221 b.Fail() 222 } 223 bufs[i] = o 224 } 225 for i := 0; i < len(bufs); i++ { 226 o := bufs[i] 227 buf := *o 228 buf = strconv.AppendInt(buf[:0], 12344321, 10) 229 runtime.KeepAlive(buf) 230 p.Put(o) 231 } 232 } 233 }) 234 } 235 236 func BenchmarkObjectPoolParallelGetMultiPutContendedWithRefill(b *testing.B) { 237 opts := NewObjectPoolOptions(). 238 SetSize(32). 239 SetRefillLowWatermark(0.05). 240 SetRefillHighWatermark(0.25) 241 242 p := NewObjectPool(opts) 243 p.Init(func() interface{} { 244 b := make([]byte, 0, 32) 245 return &b 246 }) 247 objs := make([]interface{}, 16) 248 249 b.ResetTimer() 250 b.RunParallel(func(pb *testing.PB) { 251 for pb.Next() { 252 for i := 0; i < len(objs); i++ { 253 o := p.Get() 254 objs[i] = o 255 } 256 257 for _, obj := range objs { 258 o, ok := obj.(*[]byte) 259 if !ok { 260 b.Fail() 261 } 262 buf := *o 263 buf = strconv.AppendInt(buf[:0], 12344321, 10) 264 p.Put(o) 265 } 266 } 267 }) 268 }