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  }