github.com/rudderlabs/rudder-go-kit@v0.30.0/config/reloadable_benchmark_test.go (about)

     1  package config
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  // BenchmarkReloadable/mutex-24						135314967			8.845 ns/op
    11  // BenchmarkReloadable/rw_mutex-24					132994274			8.715 ns/op
    12  // BenchmarkReloadable/reloadable_value-24				1000000000			0.6007 ns/op
    13  // BenchmarkReloadable/reloadable_custom_mutex-24		77852116			15.19 ns/op
    14  func BenchmarkReloadable(b *testing.B) {
    15  	b.Run("mutex", func(b *testing.B) {
    16  		var v reloadableMutex[int]
    17  		go func() {
    18  			for {
    19  				v.Store(1)
    20  				time.Sleep(time.Millisecond)
    21  			}
    22  		}()
    23  		for i := 0; i < b.N; i++ {
    24  			_ = v.Load()
    25  		}
    26  	})
    27  	b.Run("rw mutex", func(b *testing.B) {
    28  		var v reloadableRWMutex[int]
    29  		go func() {
    30  			for {
    31  				v.Store(1)
    32  				time.Sleep(time.Millisecond)
    33  			}
    34  		}()
    35  		for i := 0; i < b.N; i++ {
    36  			_ = v.Load()
    37  		}
    38  	})
    39  	b.Run("reloadable value", func(b *testing.B) {
    40  		var v reloadableValue[int]
    41  		go func() {
    42  			for {
    43  				v.Store(1)
    44  				time.Sleep(time.Millisecond)
    45  			}
    46  		}()
    47  		for i := 0; i < b.N; i++ {
    48  			_ = v.Load()
    49  		}
    50  	})
    51  	b.Run("reloadable custom mutex", func(b *testing.B) {
    52  		var v reloadableCustomMutex[int]
    53  		go func() {
    54  			for {
    55  				v.Store(1)
    56  				time.Sleep(time.Millisecond)
    57  			}
    58  		}()
    59  		for i := 0; i < b.N; i++ {
    60  			_ = v.Load()
    61  		}
    62  	})
    63  }
    64  
    65  type reloadableMutex[T comparable] struct {
    66  	value T
    67  	lock  sync.Mutex
    68  }
    69  
    70  func (a *reloadableMutex[T]) Load() T {
    71  	a.lock.Lock()
    72  	v := a.value
    73  	a.lock.Unlock()
    74  	return v
    75  }
    76  
    77  func (a *reloadableMutex[T]) Store(v T) {
    78  	a.lock.Lock()
    79  	a.value = v
    80  	a.lock.Unlock()
    81  }
    82  
    83  type reloadableRWMutex[T comparable] struct {
    84  	value T
    85  	lock  sync.RWMutex
    86  }
    87  
    88  func (a *reloadableRWMutex[T]) Load() T {
    89  	a.lock.RLock()
    90  	v := a.value
    91  	a.lock.RUnlock()
    92  	return v
    93  }
    94  
    95  func (a *reloadableRWMutex[T]) Store(v T) {
    96  	a.lock.Lock()
    97  	a.value = v
    98  	a.lock.Unlock()
    99  }
   100  
   101  type reloadableValue[T comparable] struct {
   102  	// Note: it would also be possible to use reloadable.Pointer to avoid the panics from
   103  	// atomic.Value but we won't be able to do the "swapIfNotEqual" as a single transaction anyway
   104  	atomic.Value
   105  }
   106  
   107  func (a *reloadableValue[T]) Load() (zero T) {
   108  	v := a.Value.Load()
   109  	if v == nil {
   110  		return zero
   111  	}
   112  	return v.(T)
   113  }
   114  
   115  func (a *reloadableValue[T]) Store(v T) {
   116  	a.Value.Store(v)
   117  }
   118  
   119  type reloadableCustomMutex[T comparable] struct {
   120  	value T
   121  	mutex int32
   122  }
   123  
   124  func (a *reloadableCustomMutex[T]) lock() {
   125  	for atomic.CompareAndSwapInt32(&a.mutex, 0, 1) {
   126  	}
   127  }
   128  
   129  func (a *reloadableCustomMutex[T]) unlock() {
   130  	for atomic.CompareAndSwapInt32(&a.mutex, 1, 0) {
   131  	}
   132  }
   133  
   134  func (a *reloadableCustomMutex[T]) Load() T {
   135  	a.lock()
   136  	v := a.value
   137  	a.unlock()
   138  	return v
   139  }
   140  
   141  func (a *reloadableCustomMutex[T]) Store(v T) {
   142  	a.lock()
   143  	a.value = v
   144  	a.unlock()
   145  }