github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/sys/syncx/rwmutex_test.go (about) 1 package syncx 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 8 "github.com/songzhibin97/go-baseutils/sys/fastrand" 9 ) 10 11 func BenchmarkRWMutex100Read(b *testing.B) { 12 for nWorker := 1; nWorker <= 256; nWorker <<= 2 { 13 b.Run(fmt.Sprintf("syncx-%d", nWorker), func(b *testing.B) { 14 benchmarkSyncxRWMutexNWriteNRead(b, nWorker, 0) 15 }) 16 17 b.Run(fmt.Sprintf("sync-%d", nWorker), func(b *testing.B) { 18 benchmarkSyncRWMutexNWriteNRead(b, nWorker, 0) 19 }) 20 } 21 } 22 23 func BenchmarkRWMutex1Write99Read(b *testing.B) { 24 for nWorker := 1; nWorker <= 256; nWorker <<= 2 { 25 b.Run(fmt.Sprintf("syncx-%d", nWorker), func(b *testing.B) { 26 benchmarkSyncxRWMutexNWriteNRead(b, nWorker, 1) 27 }) 28 29 b.Run(fmt.Sprintf("sync-%d", nWorker), func(b *testing.B) { 30 benchmarkSyncRWMutexNWriteNRead(b, nWorker, 1) 31 }) 32 } 33 } 34 35 func BenchmarkRWMutex10Write90Read(b *testing.B) { 36 for nWorker := 1; nWorker <= 256; nWorker <<= 2 { 37 b.Run(fmt.Sprintf("syncx-%d", nWorker), func(b *testing.B) { 38 benchmarkSyncxRWMutexNWriteNRead(b, nWorker, 10) 39 }) 40 41 b.Run(fmt.Sprintf("sync-%d", nWorker), func(b *testing.B) { 42 benchmarkSyncRWMutexNWriteNRead(b, nWorker, 10) 43 }) 44 } 45 } 46 47 func BenchmarkRWMutex50Write50Read(b *testing.B) { 48 for nWorker := 1; nWorker <= 256; nWorker <<= 2 { 49 b.Run(fmt.Sprintf("syncx-%d", nWorker), func(b *testing.B) { 50 benchmarkSyncxRWMutexNWriteNRead(b, nWorker, 50) 51 }) 52 53 b.Run(fmt.Sprintf("sync-%d", nWorker), func(b *testing.B) { 54 benchmarkSyncRWMutexNWriteNRead(b, nWorker, 50) 55 }) 56 } 57 } 58 59 func benchmarkSyncRWMutexNWriteNRead(b *testing.B, nWorker, nWrite int) { 60 var res int // A mock resource we contention for 61 62 var mu sync.RWMutex 63 mu.Lock() 64 65 var wg sync.WaitGroup 66 wg.Add(nWorker) 67 68 n := b.N 69 quota := n / nWorker 70 71 for g := nWorker; g > 0; g-- { 72 // Comuse remaining quota 73 if g == 1 { 74 quota = n 75 } 76 go func(quota int) { 77 for i := 0; i < quota; i++ { 78 if fastrand.Intn(100) > nWrite-1 { 79 mu.RLock() 80 _ = res 81 mu.RUnlock() 82 } else { 83 mu.Lock() 84 res++ 85 mu.Unlock() 86 } 87 } 88 wg.Done() 89 }(quota) 90 91 n -= quota 92 } 93 94 if n != 0 { 95 b.Fatalf("Incorrect quota assignments: %v remaining", n) 96 } 97 98 b.ResetTimer() 99 mu.Unlock() 100 wg.Wait() 101 } 102 103 func benchmarkSyncxRWMutexNWriteNRead(b *testing.B, nWorker, nWrite int) { 104 var res int // A mock resource we contention for 105 106 mu := NewRWMutex() 107 mu.Lock() 108 109 var wg sync.WaitGroup 110 wg.Add(nWorker) 111 112 n := b.N 113 quota := n / nWorker 114 115 for g := nWorker; g > 0; g-- { 116 // Comuse remaining quota 117 if g == 1 { 118 quota = n 119 } 120 go func(quota int) { 121 rmu := mu.RLocker() 122 for i := 0; i < quota; i++ { 123 if fastrand.Intn(100) > nWrite-1 { 124 rmu.Lock() 125 _ = res 126 rmu.Unlock() 127 } else { 128 mu.Lock() 129 res++ 130 mu.Unlock() 131 } 132 } 133 wg.Done() 134 }(quota) 135 136 n -= quota 137 } 138 139 if n != 0 { 140 b.Fatalf("Incorrect quota assignments: %v remaining", n) 141 } 142 143 b.ResetTimer() 144 mu.Unlock() 145 wg.Wait() 146 }