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 }