github.com/ergo-services/ergo@v1.999.224/lib/mpsc_test.go (about)

     1  package lib
     2  
     3  import (
     4  	"math/rand"
     5  	"runtime"
     6  	"strconv"
     7  	"sync"
     8  	"sync/atomic"
     9  	"testing"
    10  )
    11  
    12  func TestMPSCsequential(t *testing.T) {
    13  
    14  	type vv struct {
    15  		v int64
    16  	}
    17  	l := int64(10)
    18  	queue := NewQueueLimitMPSC(l)
    19  	// append to the queue
    20  	for i := int64(0); i < l; i++ {
    21  		v := vv{v: i + 100}
    22  		if queue.Push(v) == false {
    23  			t.Fatal("can't push value into the queue")
    24  		}
    25  	}
    26  	if queue.Len() != l {
    27  		t.Fatal("queue length must be 10")
    28  	}
    29  
    30  	if queue.Push("must be failed") == true {
    31  		t.Fatal("must be false: exceeded the limit", queue.Len())
    32  	}
    33  
    34  	// walking through the queue
    35  	item := queue.Item()
    36  	for i := int64(0); i < l; i++ {
    37  		v, ok := item.Value().(vv)
    38  		if ok == false || v.v != i+100 {
    39  			t.Fatal("incorrect value. expected", i+100, "got", v)
    40  		}
    41  
    42  		item = item.Next()
    43  
    44  	}
    45  	if item != nil {
    46  		t.Fatal("there is something else in the queue", item.Value())
    47  	}
    48  
    49  	// popping from the queue
    50  	for i := int64(0); i < l; i++ {
    51  		value, ok := queue.Pop()
    52  		if ok == false {
    53  			t.Fatal("there must be value")
    54  		}
    55  		v, ok := value.(vv)
    56  		if ok == false || v.v != i+100 {
    57  			t.Fatal("incorrect value. expected", i+100, "got", v)
    58  		}
    59  	}
    60  
    61  	// must be empty
    62  	if queue.Len() != 0 {
    63  		t.Fatal("queue length must be 0")
    64  	}
    65  
    66  	// check Clear method
    67  	if ok := queue.Push(vv{v: 100}); ok == false {
    68  		t.Fatal("must be true here")
    69  	}
    70  
    71  	item = queue.Item()
    72  	if item == nil {
    73  		t.Fatal("item is nil")
    74  	}
    75  	item.Clear()
    76  	value, ok := queue.Pop()
    77  	if ok == false {
    78  		t.Fatal("must be true here")
    79  	}
    80  	if value != nil {
    81  		t.Fatal("must be nil here")
    82  	}
    83  }
    84  
    85  func TestMPSCparallel(t *testing.T) {
    86  
    87  	type vv struct {
    88  		v int64
    89  	}
    90  	l := int64(100000)
    91  	queue := NewQueueLimitMPSC(l)
    92  	sum := int64(0)
    93  	// append to the queue
    94  	var wg sync.WaitGroup
    95  	for i := int64(0); i < l; i++ {
    96  		v := vv{v: i + 100}
    97  		sum += v.v
    98  		wg.Add(1)
    99  		go func(v vv) {
   100  			if queue.Push(v) == false {
   101  				t.Fatal("can't push value into the queue")
   102  			}
   103  			wg.Done()
   104  		}(v)
   105  	}
   106  	wg.Wait()
   107  	if x := queue.Len(); x != l {
   108  		t.Fatal("queue length must be", l, "have", x)
   109  	}
   110  
   111  	if queue.Push("must be failed") == true {
   112  		t.Fatal("must be false: exceeded the limit", queue.Len())
   113  	}
   114  
   115  	// walking through the queue
   116  	item := queue.Item()
   117  	sum1 := int64(0)
   118  	for i := int64(0); i < l; i++ {
   119  		v, ok := item.Value().(vv)
   120  		sum1 += v.v
   121  		if ok == false {
   122  			t.Fatal("incorrect value. got", v)
   123  		}
   124  
   125  		item = item.Next()
   126  
   127  	}
   128  	if item != nil {
   129  		t.Fatal("there is something else in the queue", item.Value())
   130  	}
   131  	if sum != sum1 {
   132  		t.Fatal("wrong value. exp", sum, "got", sum1)
   133  	}
   134  
   135  	sum1 = 0
   136  	// popping from the queue
   137  	for i := int64(0); i < l; i++ {
   138  		value, ok := queue.Pop()
   139  		if ok == false {
   140  			t.Fatal("there must be value")
   141  		}
   142  		v, ok := value.(vv)
   143  		sum1 += v.v
   144  		if ok == false {
   145  			t.Fatal("incorrect value. got", v)
   146  		}
   147  	}
   148  
   149  	// must be empty
   150  	if queue.Len() != 0 {
   151  		t.Fatal("queue length must be 0")
   152  	}
   153  	if sum != sum1 {
   154  		t.Fatal("wrong value. exp", sum, "got", sum1)
   155  	}
   156  }
   157  
   158  type chanQueue struct {
   159  	q chan interface{}
   160  }
   161  
   162  type testQueue interface {
   163  	Push(v interface{}) bool
   164  	Pop() (interface{}, bool)
   165  }
   166  
   167  func newChanQueue() *chanQueue {
   168  	chq := &chanQueue{
   169  		q: make(chan interface{}, 100000000),
   170  	}
   171  	return chq
   172  }
   173  
   174  // Enqueue puts the given value v at the tail of the queue.
   175  func (cq *chanQueue) Push(v interface{}) bool {
   176  	select {
   177  	case cq.q <- v:
   178  		return true
   179  	default:
   180  		panic("channel is full")
   181  	}
   182  }
   183  
   184  func (cq *chanQueue) Pop() (interface{}, bool) {
   185  	v := <-cq.q
   186  	return v, true
   187  }
   188  func BenchmarkMPSC(b *testing.B) {
   189  	queues := map[string]testQueue{
   190  		"Chan queue           ": newChanQueue(),
   191  		"MPSC queue           ": NewQueueMPSC(),
   192  		"MPSC with limit queue": NewQueueLimitMPSC(0),
   193  	}
   194  
   195  	length := 1 << 12
   196  	inputs := make([]int, length)
   197  	for i := 0; i < length; i++ {
   198  		inputs = append(inputs, rand.Int())
   199  	}
   200  
   201  	for _, cpus := range []int{4, 32, 1024} {
   202  		runtime.GOMAXPROCS(cpus)
   203  		for name, q := range queues {
   204  			b.Run(name+"#"+strconv.Itoa(cpus), func(b *testing.B) {
   205  				b.ResetTimer()
   206  
   207  				var c int64
   208  				b.RunParallel(func(pb *testing.PB) {
   209  					for pb.Next() {
   210  						i := int(atomic.AddInt64(&c, 1)-1) % length
   211  						v := inputs[i]
   212  						if v >= 0 {
   213  							q.Push(v)
   214  						} else {
   215  							q.Pop()
   216  						}
   217  					}
   218  				})
   219  			})
   220  		}
   221  	}
   222  }