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  }