github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/internal/queue/mpsc/mpsc_test.go (about)

     1  package mpsc
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  )
    11  
    12  func TestQueue_PushPop(t *testing.T) {
    13  	q := New()
    14  
    15  	q.Push(1)
    16  	q.Push(2)
    17  	assert.Equal(t, 1, q.Pop())
    18  	assert.Equal(t, 2, q.Pop())
    19  	assert.True(t, q.Empty())
    20  }
    21  
    22  func TestQueue_Empty(t *testing.T) {
    23  	q := New()
    24  	assert.True(t, q.Empty())
    25  	q.Push(1)
    26  	assert.False(t, q.Empty())
    27  }
    28  
    29  func TestQueue_PushPopOneProducer(t *testing.T) {
    30  	expCount := 100
    31  
    32  	var wg sync.WaitGroup
    33  	wg.Add(1)
    34  	q := New()
    35  	go func() {
    36  		i := 0
    37  		for {
    38  			r := q.Pop()
    39  			if r == nil {
    40  				runtime.Gosched()
    41  				continue
    42  			}
    43  			i++
    44  			if i == expCount {
    45  				wg.Done()
    46  				return
    47  			}
    48  		}
    49  	}()
    50  
    51  	var val interface{} = "foo"
    52  
    53  	for i := 0; i < expCount; i++ {
    54  		q.Push(val)
    55  	}
    56  
    57  	wg.Wait()
    58  }
    59  
    60  //func TestMpscQueueConsistency(t *testing.T) {
    61  //	max := 1000000
    62  //	c := runtime.NumCPU() / 2
    63  //	cmax := max / c
    64  //	var wg sync.WaitGroup
    65  //	wg.Add(1)
    66  //	q := New()
    67  //
    68  //	go func() {
    69  //		i := 0
    70  //		seen := make(map[string]string)
    71  //		for {
    72  //			r := q.Pop()
    73  //			if r == nil {
    74  //				runtime.Gosched()
    75  //
    76  //				continue
    77  //			}
    78  //			i++
    79  //			s, _ := r.(string)
    80  //			_, present := seen[s]
    81  //			if present {
    82  //				log.Printf("item have already been seen %v", s)
    83  //				t.FailNow()
    84  //			}
    85  //			seen[s] = s
    86  //			if i == cmax*c {
    87  //				wg.Done()
    88  //				return
    89  //			}
    90  //		}
    91  //	}()
    92  //
    93  //	for j := 0; j < c; j++ {
    94  //		jj := j
    95  //		go func() {
    96  //			for i := 0; i < cmax; i++ {
    97  //				if rand.Intn(10) == 0 {
    98  //					time.Sleep(time.Duration(rand.Intn(1000)))
    99  //				}
   100  //				q.Push(fmt.Sprintf("%v %v", jj, i))
   101  //			}
   102  //		}()
   103  //	}
   104  //
   105  //	wg.Wait()
   106  //	time.Sleep(500 * time.Millisecond)
   107  //	// queue should be empty
   108  //	for i := 0; i < 100; i++ {
   109  //		r := q.Pop()
   110  //		if r != nil {
   111  //			log.Printf("unexpected result %+v", r)
   112  //			t.FailNow()
   113  //		}
   114  //	}
   115  //}
   116  
   117  func benchmarkPushPop(count, c int) {
   118  	var wg sync.WaitGroup
   119  	wg.Add(1)
   120  	q := New()
   121  	go func() {
   122  		i := 0
   123  		for {
   124  			r := q.Pop()
   125  			if r == nil {
   126  				runtime.Gosched()
   127  				continue
   128  			}
   129  			i++
   130  			if i == count {
   131  				wg.Done()
   132  				return
   133  			}
   134  		}
   135  	}()
   136  
   137  	var val interface{} = "foo"
   138  
   139  	for i := 0; i < c; i++ {
   140  		go func(n int) {
   141  			for n > 0 {
   142  				q.Push(val)
   143  				n--
   144  			}
   145  		}(count / c)
   146  	}
   147  
   148  	wg.Wait()
   149  }
   150  
   151  func benchmarkChannelPushPop(count, c int) {
   152  	var wg sync.WaitGroup
   153  	wg.Add(1)
   154  	ch := make(chan interface{}, 100)
   155  	go func() {
   156  		i := 0
   157  		for {
   158  			<-ch
   159  			i++
   160  			if i == count {
   161  				wg.Done()
   162  				return
   163  			}
   164  		}
   165  	}()
   166  
   167  	var val interface{} = "foo"
   168  
   169  	for i := 0; i < c; i++ {
   170  		go func(n int) {
   171  			for n > 0 {
   172  				ch <- val
   173  				n--
   174  			}
   175  		}(count / c)
   176  	}
   177  }
   178  
   179  func BenchmarkPushPop(b *testing.B) {
   180  	benchmarks := []struct {
   181  		count       int
   182  		concurrency int
   183  	}{
   184  		{
   185  			count:       10000,
   186  			concurrency: 1,
   187  		},
   188  		{
   189  			count:       10000,
   190  			concurrency: 2,
   191  		},
   192  		{
   193  			count:       10000,
   194  			concurrency: 4,
   195  		},
   196  		{
   197  			count:       10000,
   198  			concurrency: 8,
   199  		},
   200  	}
   201  	for _, bm := range benchmarks {
   202  		b.Run(fmt.Sprintf("%d_%d", bm.count, bm.concurrency), func(b *testing.B) {
   203  			for i := 0; i < b.N; i++ {
   204  				benchmarkPushPop(bm.count, bm.concurrency)
   205  			}
   206  		})
   207  	}
   208  }
   209  
   210  func BenchmarkChannelPushPop(b *testing.B) {
   211  	benchmarks := []struct {
   212  		count       int
   213  		concurrency int
   214  	}{
   215  		{
   216  			count:       10000,
   217  			concurrency: 1,
   218  		},
   219  		{
   220  			count:       10000,
   221  			concurrency: 2,
   222  		},
   223  		{
   224  			count:       10000,
   225  			concurrency: 4,
   226  		},
   227  		{
   228  			count:       10000,
   229  			concurrency: 8,
   230  		},
   231  	}
   232  	for _, bm := range benchmarks {
   233  		b.Run(fmt.Sprintf("%d_%d", bm.count, bm.concurrency), func(b *testing.B) {
   234  			for i := 0; i < b.N; i++ {
   235  				benchmarkChannelPushPop(bm.count, bm.concurrency)
   236  			}
   237  		})
   238  	}
   239  }