github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/chan_test.go (about)

     1  package f_test
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/angenalZZZ/gofunc/f"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  var benchmarks bool
    14  
    15  func init() {
    16  	benchmarks = os.Getenv("CHANNEL_BENCH") == "1"
    17  	if !benchmarks {
    18  		fmt.Printf("Use CHANNEL_BENCH=1 for benchmarks\n")
    19  	}
    20  }
    21  
    22  func TestOrder(t *testing.T) {
    23  	// testing that order is preserved
    24  	type msgT struct{ i, thread int }
    25  	ch := f.MakeChannel(0)
    26  	N := 1000000
    27  	T := 100
    28  	go func() {
    29  		f.Ops(N, 100, func(i, thread int) {
    30  			if !ch.Send(&msgT{i, thread}) {
    31  				panic("bad news")
    32  			}
    33  		})
    34  		ch.Close()
    35  	}()
    36  	// create unique buckets per thread and store each message
    37  	// sequentially in their respective bucket.
    38  	m := make(map[int][]int)
    39  	for {
    40  		v, ok := ch.Recv()
    41  		if !ok {
    42  			break
    43  		}
    44  		msg := v.(*msgT)
    45  		m[msg.thread] = append(m[msg.thread], msg.i)
    46  	}
    47  	// check that each bucket contains ordered data check for duplicates
    48  	all := make(map[int]bool)
    49  	for thread := 0; thread < T; thread++ {
    50  		b, ok := m[thread]
    51  		// println(thread, m[thread])
    52  		// continue
    53  		if !ok {
    54  			t.Fatal("missing bucket")
    55  		}
    56  		if len(b) != N/T {
    57  			t.Fatal("invalid bucket size")
    58  		}
    59  		h := -1
    60  		for i := 0; i < len(b); i++ {
    61  			if b[i] <= h {
    62  				t.Fatal("out of order")
    63  			}
    64  			h = b[i]
    65  			if all[h] {
    66  				t.Fatal("duplicate value")
    67  			}
    68  			all[h] = true
    69  		}
    70  	}
    71  }
    72  
    73  func fixLeft(s string, n int) string {
    74  	return (s + strings.Repeat(" ", n))[:n]
    75  }
    76  func fixRight(s string, n int) string {
    77  	return (strings.Repeat(" ", n) + s)[len(s):]
    78  }
    79  
    80  func printResults(key string, N, P int, dur time.Duration) {
    81  	s := fixLeft(key, 13) + " "
    82  	s += fixLeft(fmt.Sprintf("%d ops in %dms", N, int(dur.Seconds()*1000)), 22) + " "
    83  	s += fixRight(fmt.Sprintf("%d/sec", int(float64(N)/dur.Seconds())), 12) + " "
    84  	s += fixRight(fmt.Sprintf("%dns/op", int(dur/time.Duration(N))), 10) + " "
    85  	s += fixRight(fmt.Sprintf("%s %4d producer", (s + strings.Repeat(" ", 100))[:60], P), 14)
    86  	fmt.Printf("%s\n", strings.TrimSpace(s))
    87  }
    88  
    89  func TestChannelUnbuffered(t *testing.T) {
    90  	N := 1000000
    91  	for P := 1; P < 1000; P *= 10 {
    92  		start := time.Now()
    93  		benchmarkChannel(N, 0, P, false)
    94  		if benchmarks {
    95  			printResults("channel(0)", N, P, time.Since(start))
    96  		}
    97  	}
    98  }
    99  
   100  func TestChannel10Unbuffered(t *testing.T) {
   101  	N := 1000000
   102  	for P := 1; P < 1000; P *= 10 {
   103  		start := time.Now()
   104  		benchmarkChannel(N, 10, P, false)
   105  		if benchmarks {
   106  			printResults("channel(10)", N, P, time.Since(start))
   107  		}
   108  	}
   109  }
   110  
   111  func TestChannel100Unbuffered(t *testing.T) {
   112  	N := 1000000
   113  	for P := 1; P < 1000; P *= 10 {
   114  		start := time.Now()
   115  		benchmarkChannel(N, 100, P, false)
   116  		if benchmarks {
   117  			printResults("channel(100)", N, P, time.Since(start))
   118  		}
   119  	}
   120  }
   121  
   122  func TestGoChanUnbuffered(t *testing.T) {
   123  	if !benchmarks {
   124  		return
   125  	}
   126  	N := 1000000
   127  	var start time.Time
   128  	for P := 1; P < 1000; P *= 10 {
   129  		start = time.Now()
   130  		benchmarkGoChan(N, 0, P, false)
   131  		printResults("go-chan(0)", N, P, time.Since(start))
   132  	}
   133  }
   134  
   135  func TestGoChan10(t *testing.T) {
   136  	if !benchmarks {
   137  		return
   138  	}
   139  	N := 1000000
   140  	var start time.Time
   141  	for P := 1; P < 1000; P *= 10 {
   142  		start = time.Now()
   143  		benchmarkGoChan(N, 10, P, false)
   144  		printResults("go-chan(10)", N, P, time.Since(start))
   145  	}
   146  }
   147  
   148  func TestGoChan100(t *testing.T) {
   149  	if !benchmarks {
   150  		return
   151  	}
   152  	N := 1000000
   153  	var start time.Time
   154  	for P := 1; P < 1000; P *= 10 {
   155  		start = time.Now()
   156  		benchmarkGoChan(N, 100, P, false)
   157  		printResults("go-chan(100)", N, P, time.Since(start))
   158  	}
   159  }
   160  
   161  func benchmarkChannel(N int, buffered int, P int, validate bool) {
   162  	ch := f.MakeChannel(buffered)
   163  	var wg sync.WaitGroup
   164  	wg.Add(1)
   165  	go func() {
   166  		for i := 0; i < N; i++ {
   167  			v, _ := ch.Recv()
   168  			if validate {
   169  				if v != uint64(i) {
   170  					panic("out of order")
   171  				}
   172  			}
   173  		}
   174  		wg.Done()
   175  	}()
   176  	f.Ops(N, P, func(i, _ int) {
   177  		ch.Send(uint64(i))
   178  	})
   179  	wg.Wait()
   180  }
   181  
   182  func benchmarkGoChan(N, buffered int, producers int, validate bool) {
   183  	ch := make(chan uint64, buffered)
   184  	var wg sync.WaitGroup
   185  	wg.Add(1)
   186  	go func() {
   187  		for i := 0; i < N; i++ {
   188  			v := <-ch
   189  			if validate {
   190  				if v != uint64(i) {
   191  					panic("out of order")
   192  				}
   193  			}
   194  		}
   195  		wg.Done()
   196  	}()
   197  	f.Ops(N, producers, func(i, _ int) {
   198  		ch <- uint64(i)
   199  	})
   200  	wg.Wait()
   201  }
   202  
   203  func Benchmark100ProducerChannel100(b *testing.B) {
   204  	b.ReportAllocs()
   205  	benchmarkChannel(b.N, 100, 100, false)
   206  }
   207  
   208  func Benchmark100ProducerChannel10(b *testing.B) {
   209  	b.ReportAllocs()
   210  	benchmarkChannel(b.N, 10, 100, false)
   211  }
   212  
   213  func Benchmark100ProducerChannelUnbuffered(b *testing.B) {
   214  	b.ReportAllocs()
   215  	benchmarkChannel(b.N, 0, 100, false)
   216  }
   217  
   218  func Benchmark100ProducerGoChan100(b *testing.B) {
   219  	b.ReportAllocs()
   220  	benchmarkGoChan(b.N, 100, 100, false)
   221  }
   222  
   223  func Benchmark100ProducerGoChan10(b *testing.B) {
   224  	b.ReportAllocs()
   225  	benchmarkGoChan(b.N, 10, 100, false)
   226  }
   227  
   228  func Benchmark100ProducerGoChanUnbuffered(b *testing.B) {
   229  	b.ReportAllocs()
   230  	benchmarkGoChan(b.N, 0, 100, false)
   231  }