github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/queue/ring_buffer_cursor_test.go (about) 1 package queue 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "testing" 7 "time" 8 "unsafe" 9 ) 10 11 func TestXRingBufferCursor(t *testing.T) { 12 cursor := NewXRingBufferCursor() 13 beginIs := time.Now() 14 for i := 0; i < 100000000; i++ { 15 x := cursor.Next() 16 if x%10000000 == 0 { 17 t.Logf("x=%d", x) 18 } 19 } 20 diff := time.Since(beginIs) 21 t.Logf("ts diff=%v", diff) 22 } 23 24 func TestXRingBufferCursorConcurrency(t *testing.T) { 25 // lower than single goroutine test 26 cursor := NewXRingBufferCursor() 27 t.Logf("cursor size=%v", unsafe.Sizeof(*cursor.(*rbCursor))) 28 beginIs := time.Now() 29 wg := sync.WaitGroup{} 30 wg.Add(10000) 31 for i := 0; i < 10000; i++ { 32 go func(idx int) { 33 for j := 0; j < 10000; j++ { 34 x := cursor.Next() 35 if x%10000000 == 0 { 36 t.Logf("gid=%d, x=%d", idx, x) 37 } 38 } 39 wg.Done() 40 }(i) 41 } 42 wg.Wait() 43 diff := time.Since(beginIs) 44 t.Logf("ts diff=%v", diff) 45 } 46 47 func TestXRingBufferCursorNoPaddingConcurrency(t *testing.T) { 48 // Better than padding version 49 var cursor uint64 // same address, meaningless for data race 50 beginIs := time.Now() 51 wg := sync.WaitGroup{} 52 wg.Add(10000) 53 for i := 0; i < 10000; i++ { 54 go func(idx int) { 55 for j := 0; j < 10000; j++ { 56 x := atomic.AddUint64(&cursor, 1) 57 if x%10000000 == 0 { 58 t.Logf("gid=%d, x=%d", idx, x) 59 } 60 } 61 wg.Done() 62 }(i) 63 } 64 wg.Wait() 65 diff := time.Since(beginIs) 66 t.Logf("ts diff=%v", diff) 67 } 68 69 type noPadObj struct { 70 a, b, c uint64 71 } 72 73 func (o *noPadObj) increase() { 74 atomic.AddUint64(&o.a, 1) 75 atomic.AddUint64(&o.b, 1) 76 atomic.AddUint64(&o.c, 1) 77 } 78 79 type padObj struct { 80 a uint64 81 _ [8]uint64 82 b uint64 83 _ [8]uint64 84 c uint64 85 _ [8]uint64 86 } 87 88 func (o *padObj) increase() { 89 atomic.AddUint64(&o.a, 1) 90 atomic.AddUint64(&o.b, 1) 91 atomic.AddUint64(&o.c, 1) 92 } 93 94 // goos: linux 95 // goarch: amd64 96 // pkg: github.com/benz9527/toy-box/algo/queue 97 // cpu: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz 98 // BenchmarkNoPadObj 99 // BenchmarkNoPadObj-4 15984717 79.60 ns/op 100 func BenchmarkNoPadObj(b *testing.B) { 101 // Lower than padding version 102 obj := &noPadObj{} 103 b.RunParallel(func(pb *testing.PB) { 104 for pb.Next() { 105 obj.increase() 106 } 107 }) 108 } 109 110 // goos: linux 111 // goarch: amd64 112 // pkg: github.com/benz9527/toy-box/algo/queue 113 // cpu: Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz 114 // BenchmarkPadObj 115 // BenchmarkPadObj-4 19536519 62.11 ns/op 116 func BenchmarkPadObj(b *testing.B) { 117 obj := &padObj{} 118 b.RunParallel(func(pb *testing.PB) { 119 for pb.Next() { 120 obj.increase() 121 } 122 }) 123 } 124 125 func TestFalseSharing(t *testing.T) { 126 // In the same cache line 127 volatileArray := [4]uint64{0, 0, 0, 0} // contiguous memory 128 var wg sync.WaitGroup 129 wg.Add(4) 130 beginIs := time.Now() 131 for i := 0; i < 4; i++ { 132 // Concurrent write to the same cache line 133 // Many cache misses, because of many RFO 134 go func(idx int) { 135 for j := 0; j < 100000000; j++ { 136 atomic.AddUint64(&volatileArray[idx], 1) 137 } 138 wg.Done() 139 }(i) 140 } 141 wg.Wait() 142 diff := time.Since(beginIs) 143 // ts diff=8.423525377s 144 t.Logf("ts diff=%v", diff) 145 } 146 147 func TestNoFalseSharing(t *testing.T) { 148 type padE struct { 149 value uint64 150 //_ [7]uint64 151 _ [CacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte 152 } 153 154 // Each one in a different cache line 155 volatileArray := [4]padE{{}, {}, {}, {}} 156 var wg sync.WaitGroup 157 wg.Add(4) 158 beginIs := time.Now() 159 for i := 0; i < 4; i++ { 160 // No RFO data race 161 go func(idx int) { 162 for j := 0; j < 100000000; j++ { 163 atomic.AddUint64(&volatileArray[idx].value, 1) 164 } 165 wg.Done() 166 }(i) 167 } 168 wg.Wait() 169 diff := time.Since(beginIs) 170 // ts diff=890.219393ms 171 t.Logf("ts diff=%v", diff) 172 }