github.com/alphadose/zenq/v2@v2.8.4/benchmarks/e2e/benchmark_test.go (about)

     1  package zenq_test
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/alphadose/zenq/v2"
    10  )
    11  
    12  const bufferSize = 1 << 12
    13  
    14  type Payload struct {
    15  	first   byte
    16  	second  int64
    17  	third   float64
    18  	fourth  string
    19  	fifth   complex64
    20  	sixth   []rune
    21  	seventh bool
    22  }
    23  
    24  type test struct {
    25  	writers   int
    26  	readers   int
    27  	inputSize int
    28  }
    29  
    30  var testCases = []test{
    31  	{writers: 1, readers: 1, inputSize: 1e3},
    32  	{writers: 3, readers: 3, inputSize: 3e3},
    33  	{writers: 8, readers: 8, inputSize: 8e3},
    34  	{writers: bufferSize * 2, readers: 1, inputSize: bufferSize * 2 * 4},
    35  	{writers: 1, readers: bufferSize * 2, inputSize: bufferSize * 2 * 4},
    36  	{writers: 100, readers: 100, inputSize: 6e6},
    37  	{writers: 1e3, readers: 1e3, inputSize: 7e6},
    38  }
    39  
    40  func init() {
    41  	for _, t := range testCases {
    42  		if t.inputSize%t.writers != 0 {
    43  			panic(fmt.Sprintf("input size %d should be dividable by writers %d", t.inputSize, t.writers))
    44  		}
    45  		if t.inputSize%t.readers != 0 {
    46  			panic(fmt.Sprintf("input size %d should be dividable by readers %d", t.inputSize, t.readers))
    47  		}
    48  	}
    49  }
    50  
    51  func BenchmarkChan_ProduceConsume(b *testing.B) {
    52  	for _, t := range testCases {
    53  		t := t
    54  		b.Run(fmt.Sprintf("W%d/R%d/Size%d", t.writers, t.readers, t.inputSize), func(b *testing.B) {
    55  			for i := 0; i < b.N; i++ {
    56  				benchmarkProduceConsumeChan(b, t)
    57  			}
    58  		})
    59  	}
    60  }
    61  
    62  func benchmarkProduceConsumeChan(b *testing.B, t test) {
    63  	q := make(chan Payload, bufferSize)
    64  	defer runtime.KeepAlive(q)
    65  
    66  	writesPerProducer := t.inputSize / t.writers
    67  	readsPerConsumer := t.inputSize / t.readers
    68  
    69  	var wg sync.WaitGroup
    70  	wg.Add(t.writers)
    71  
    72  	// b.ResetTimer()
    73  
    74  	for writer := 0; writer < t.writers; writer++ {
    75  		go func() {
    76  			defer wg.Done()
    77  			for i := 0; i < writesPerProducer; i++ {
    78  				q <- Payload{}
    79  			}
    80  		}()
    81  	}
    82  
    83  	wg.Add(t.readers)
    84  	for reader := 0; reader < t.readers; reader++ {
    85  		go func() {
    86  			defer wg.Done()
    87  			for i := 0; i < readsPerConsumer; i++ {
    88  				<-q
    89  			}
    90  		}()
    91  	}
    92  
    93  	wg.Wait()
    94  }
    95  
    96  func BenchmarkZenQ_ProduceConsume(b *testing.B) {
    97  	for _, t := range testCases {
    98  		t := t
    99  		b.Run(fmt.Sprintf("W%d/R%d/Size%d", t.writers, t.readers, t.inputSize), func(b *testing.B) {
   100  			for i := 0; i < b.N; i++ {
   101  				benchmarkProduceConsumeZenQ(b, t)
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func benchmarkProduceConsumeZenQ(b *testing.B, t test) {
   108  	q := zenq.New[Payload](bufferSize)
   109  	defer runtime.KeepAlive(q)
   110  
   111  	writesPerProducer := t.inputSize / t.writers
   112  	readsPerConsumer := t.inputSize / t.readers
   113  
   114  	var wg sync.WaitGroup
   115  	wg.Add(t.writers)
   116  
   117  	// b.ResetTimer()
   118  
   119  	for writer := 0; writer < t.writers; writer++ {
   120  		go func() {
   121  			defer wg.Done()
   122  			for i := 0; i < writesPerProducer; i++ {
   123  				q.Write(Payload{})
   124  			}
   125  		}()
   126  	}
   127  
   128  	wg.Add(t.readers)
   129  	for reader := 0; reader < t.readers; reader++ {
   130  		go func() {
   131  			defer wg.Done()
   132  			for i := 0; i < readsPerConsumer; i++ {
   133  				q.Read()
   134  			}
   135  		}()
   136  	}
   137  
   138  	wg.Wait()
   139  }
   140  
   141  func BenchmarkChan_New(b *testing.B) {
   142  	b.Run("struct{}", func(b *testing.B) {
   143  		b.ReportAllocs()
   144  		for i := 0; i < b.N; i++ {
   145  			ch := make(chan struct{}, bufferSize)
   146  			runtime.KeepAlive(ch)
   147  		}
   148  	})
   149  	b.Run("byte", func(b *testing.B) {
   150  		b.ReportAllocs()
   151  		for i := 0; i < b.N; i++ {
   152  			ch := make(chan byte, bufferSize)
   153  			runtime.KeepAlive(ch)
   154  		}
   155  	})
   156  	b.Run("int64", func(b *testing.B) {
   157  		b.ReportAllocs()
   158  		for i := 0; i < b.N; i++ {
   159  			ch := make(chan int64, bufferSize)
   160  			runtime.KeepAlive(ch)
   161  		}
   162  	})
   163  }
   164  
   165  func BenchmarkZenQ_New(b *testing.B) {
   166  	b.Run("struct{}", func(b *testing.B) {
   167  		b.ReportAllocs()
   168  		for i := 0; i < b.N; i++ {
   169  			zq := zenq.New[struct{}](bufferSize)
   170  			runtime.KeepAlive(zq)
   171  		}
   172  	})
   173  	b.Run("byte", func(b *testing.B) {
   174  		b.ReportAllocs()
   175  		for i := 0; i < b.N; i++ {
   176  			zq := zenq.New[byte](bufferSize)
   177  			runtime.KeepAlive(zq)
   178  		}
   179  	})
   180  	b.Run("int64", func(b *testing.B) {
   181  		b.ReportAllocs()
   182  		for i := 0; i < b.N; i++ {
   183  			zq := zenq.New[int64](bufferSize)
   184  			runtime.KeepAlive(zq)
   185  		}
   186  	})
   187  }
   188  
   189  func BenchmarkZenQ_BackgroundSelectWait(b *testing.B) {
   190  	const N = 1e4
   191  	q := zenq.New[struct{}](bufferSize)
   192  
   193  	// create background waiters
   194  	for i := 0; i < N; i++ {
   195  		go func() {
   196  			alt := zenq.New[struct{}](bufferSize)
   197  			zenq.Select(q, alt)
   198  		}()
   199  	}
   200  
   201  	b.ResetTimer()
   202  
   203  	a := zenq.New[int](bufferSize)
   204  	for i := 0; i < b.N; i++ {
   205  		a.Write(i)
   206  		runtime.Gosched()
   207  		a.Read()
   208  	}
   209  
   210  	// release background waiters
   211  	for i := 0; i < N; i++ {
   212  		q.Write(struct{}{})
   213  	}
   214  }
   215  
   216  func BenchmarkChan_BackgroundSelectWait(b *testing.B) {
   217  	const N = 1e4
   218  	q := make(chan struct{})
   219  
   220  	// create background waiters
   221  	for i := 0; i < N; i++ {
   222  		go func() {
   223  			x := make(chan struct{})
   224  			select {
   225  			case <-q:
   226  			case <-x:
   227  			}
   228  		}()
   229  	}
   230  
   231  	b.ResetTimer()
   232  
   233  	a := make(chan int, bufferSize)
   234  	for i := 0; i < b.N; i++ {
   235  		a <- i
   236  		runtime.Gosched()
   237  		<-a
   238  	}
   239  
   240  	// release background waiters
   241  	for i := 0; i < N; i++ {
   242  		q <- struct{}{}
   243  	}
   244  }