github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gofrontend/libgo/go/sync/mutex_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // GOMAXPROCS=10 go test
     6  
     7  package sync_test
     8  
     9  import (
    10  	"runtime"
    11  	. "sync"
    12  	"testing"
    13  )
    14  
    15  func HammerSemaphore(s *uint32, loops int, cdone chan bool) {
    16  	for i := 0; i < loops; i++ {
    17  		Runtime_Semacquire(s)
    18  		Runtime_Semrelease(s)
    19  	}
    20  	cdone <- true
    21  }
    22  
    23  func TestSemaphore(t *testing.T) {
    24  	s := new(uint32)
    25  	*s = 1
    26  	c := make(chan bool)
    27  	for i := 0; i < 10; i++ {
    28  		go HammerSemaphore(s, 1000, c)
    29  	}
    30  	for i := 0; i < 10; i++ {
    31  		<-c
    32  	}
    33  }
    34  
    35  func BenchmarkUncontendedSemaphore(b *testing.B) {
    36  	s := new(uint32)
    37  	*s = 1
    38  	HammerSemaphore(s, b.N, make(chan bool, 2))
    39  }
    40  
    41  func BenchmarkContendedSemaphore(b *testing.B) {
    42  	b.StopTimer()
    43  	s := new(uint32)
    44  	*s = 1
    45  	c := make(chan bool)
    46  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
    47  	b.StartTimer()
    48  
    49  	go HammerSemaphore(s, b.N/2, c)
    50  	go HammerSemaphore(s, b.N/2, c)
    51  	<-c
    52  	<-c
    53  }
    54  
    55  func HammerMutex(m *Mutex, loops int, cdone chan bool) {
    56  	for i := 0; i < loops; i++ {
    57  		m.Lock()
    58  		m.Unlock()
    59  	}
    60  	cdone <- true
    61  }
    62  
    63  func TestMutex(t *testing.T) {
    64  	m := new(Mutex)
    65  	c := make(chan bool)
    66  	for i := 0; i < 10; i++ {
    67  		go HammerMutex(m, 1000, c)
    68  	}
    69  	for i := 0; i < 10; i++ {
    70  		<-c
    71  	}
    72  }
    73  
    74  func TestMutexPanic(t *testing.T) {
    75  	defer func() {
    76  		if recover() == nil {
    77  			t.Fatalf("unlock of unlocked mutex did not panic")
    78  		}
    79  	}()
    80  
    81  	var mu Mutex
    82  	mu.Lock()
    83  	mu.Unlock()
    84  	mu.Unlock()
    85  }
    86  
    87  func BenchmarkMutexUncontended(b *testing.B) {
    88  	type PaddedMutex struct {
    89  		Mutex
    90  		pad [128]uint8
    91  	}
    92  	b.RunParallel(func(pb *testing.PB) {
    93  		var mu PaddedMutex
    94  		for pb.Next() {
    95  			mu.Lock()
    96  			mu.Unlock()
    97  		}
    98  	})
    99  }
   100  
   101  func benchmarkMutex(b *testing.B, slack, work bool) {
   102  	var mu Mutex
   103  	if slack {
   104  		b.SetParallelism(10)
   105  	}
   106  	b.RunParallel(func(pb *testing.PB) {
   107  		foo := 0
   108  		for pb.Next() {
   109  			mu.Lock()
   110  			mu.Unlock()
   111  			if work {
   112  				for i := 0; i < 100; i++ {
   113  					foo *= 2
   114  					foo /= 2
   115  				}
   116  			}
   117  		}
   118  		_ = foo
   119  	})
   120  }
   121  
   122  func BenchmarkMutex(b *testing.B) {
   123  	benchmarkMutex(b, false, false)
   124  }
   125  
   126  func BenchmarkMutexSlack(b *testing.B) {
   127  	benchmarkMutex(b, true, false)
   128  }
   129  
   130  func BenchmarkMutexWork(b *testing.B) {
   131  	benchmarkMutex(b, false, true)
   132  }
   133  
   134  func BenchmarkMutexWorkSlack(b *testing.B) {
   135  	benchmarkMutex(b, true, true)
   136  }
   137  
   138  func BenchmarkMutexNoSpin(b *testing.B) {
   139  	// This benchmark models a situation where spinning in the mutex should be
   140  	// non-profitable and allows to confirm that spinning does not do harm.
   141  	// To achieve this we create excess of goroutines most of which do local work.
   142  	// These goroutines yield during local work, so that switching from
   143  	// a blocked goroutine to other goroutines is profitable.
   144  	// As a matter of fact, this benchmark still triggers some spinning in the mutex.
   145  	var m Mutex
   146  	var acc0, acc1 uint64
   147  	b.SetParallelism(4)
   148  	b.RunParallel(func(pb *testing.PB) {
   149  		c := make(chan bool)
   150  		var data [4 << 10]uint64
   151  		for i := 0; pb.Next(); i++ {
   152  			if i%4 == 0 {
   153  				m.Lock()
   154  				acc0 -= 100
   155  				acc1 += 100
   156  				m.Unlock()
   157  			} else {
   158  				for i := 0; i < len(data); i += 4 {
   159  					data[i]++
   160  				}
   161  				// Elaborate way to say runtime.Gosched
   162  				// that does not put the goroutine onto global runq.
   163  				go func() {
   164  					c <- true
   165  				}()
   166  				<-c
   167  			}
   168  		}
   169  	})
   170  }
   171  
   172  func BenchmarkMutexSpin(b *testing.B) {
   173  	// This benchmark models a situation where spinning in the mutex should be
   174  	// profitable. To achieve this we create a goroutine per-proc.
   175  	// These goroutines access considerable amount of local data so that
   176  	// unnecessary rescheduling is penalized by cache misses.
   177  	var m Mutex
   178  	var acc0, acc1 uint64
   179  	b.RunParallel(func(pb *testing.PB) {
   180  		var data [16 << 10]uint64
   181  		for i := 0; pb.Next(); i++ {
   182  			m.Lock()
   183  			acc0 -= 100
   184  			acc1 += 100
   185  			m.Unlock()
   186  			for i := 0; i < len(data); i += 4 {
   187  				data[i]++
   188  			}
   189  		}
   190  	})
   191  }