| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | // Copyright 2017 The Go Authors. All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 15:49:15 -04:00
										 |  |  | package semaphore_test | 
					
						
							| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-11-07 16:20:06 -08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 15:49:15 -04:00
										 |  |  | 	"golang.org/x/sync/semaphore" | 
					
						
							| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // weighted is an interface matching a subset of *Weighted.  It allows | 
					
						
							|  |  |  | // alternate implementations for testing and benchmarking. | 
					
						
							|  |  |  | type weighted interface { | 
					
						
							|  |  |  | 	Acquire(context.Context, int64) error | 
					
						
							|  |  |  | 	TryAcquire(int64) bool | 
					
						
							|  |  |  | 	Release(int64) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // semChan implements Weighted using a channel for | 
					
						
							|  |  |  | // comparing against the condition variable-based implementation. | 
					
						
							|  |  |  | type semChan chan struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newSemChan(n int64) semChan { | 
					
						
							|  |  |  | 	return semChan(make(chan struct{}, n)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s semChan) Acquire(_ context.Context, n int64) error { | 
					
						
							|  |  |  | 	for i := int64(0); i < n; i++ { | 
					
						
							|  |  |  | 		s <- struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s semChan) TryAcquire(n int64) bool { | 
					
						
							|  |  |  | 	if int64(len(s))+n > int64(cap(s)) { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := int64(0); i < n; i++ { | 
					
						
							|  |  |  | 		s <- struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s semChan) Release(n int64) { | 
					
						
							|  |  |  | 	for i := int64(0); i < n; i++ { | 
					
						
							|  |  |  | 		<-s | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // acquireN calls Acquire(size) on sem N times and then calls Release(size) N times. | 
					
						
							|  |  |  | func acquireN(b *testing.B, sem weighted, size int64, N int) { | 
					
						
							|  |  |  | 	b.ResetTimer() | 
					
						
							|  |  |  | 	for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 		for j := 0; j < N; j++ { | 
					
						
							|  |  |  | 			sem.Acquire(context.Background(), size) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := 0; j < N; j++ { | 
					
						
							|  |  |  | 			sem.Release(size) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tryAcquireN calls TryAcquire(size) on sem N times and then calls Release(size) N times. | 
					
						
							|  |  |  | func tryAcquireN(b *testing.B, sem weighted, size int64, N int) { | 
					
						
							|  |  |  | 	b.ResetTimer() | 
					
						
							|  |  |  | 	for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 		for j := 0; j < N; j++ { | 
					
						
							|  |  |  | 			if !sem.TryAcquire(size) { | 
					
						
							|  |  |  | 				b.Fatalf("TryAcquire(%v) = false, want true", size) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := 0; j < N; j++ { | 
					
						
							|  |  |  | 			sem.Release(size) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkNewSeq(b *testing.B) { | 
					
						
							|  |  |  | 	for _, cap := range []int64{1, 128} { | 
					
						
							|  |  |  | 		b.Run(fmt.Sprintf("Weighted-%d", cap), func(b *testing.B) { | 
					
						
							|  |  |  | 			for i := 0; i < b.N; i++ { | 
					
						
							| 
									
										
										
										
											2017-11-01 15:49:15 -04:00
										 |  |  | 				_ = semaphore.NewWeighted(cap) | 
					
						
							| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		b.Run(fmt.Sprintf("semChan-%d", cap), func(b *testing.B) { | 
					
						
							|  |  |  | 			for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 				_ = newSemChan(cap) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkAcquireSeq(b *testing.B) { | 
					
						
							|  |  |  | 	for _, c := range []struct { | 
					
						
							|  |  |  | 		cap, size int64 | 
					
						
							|  |  |  | 		N         int | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{1, 1, 1}, | 
					
						
							|  |  |  | 		{2, 1, 1}, | 
					
						
							|  |  |  | 		{16, 1, 1}, | 
					
						
							|  |  |  | 		{128, 1, 1}, | 
					
						
							|  |  |  | 		{2, 2, 1}, | 
					
						
							|  |  |  | 		{16, 2, 8}, | 
					
						
							|  |  |  | 		{128, 2, 64}, | 
					
						
							|  |  |  | 		{2, 1, 2}, | 
					
						
							|  |  |  | 		{16, 8, 2}, | 
					
						
							|  |  |  | 		{128, 64, 2}, | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		for _, w := range []struct { | 
					
						
							|  |  |  | 			name string | 
					
						
							|  |  |  | 			w    weighted | 
					
						
							|  |  |  | 		}{ | 
					
						
							| 
									
										
										
										
											2017-11-01 15:49:15 -04:00
										 |  |  | 			{"Weighted", semaphore.NewWeighted(c.cap)}, | 
					
						
							| 
									
										
										
										
											2017-03-17 13:13:11 -04:00
										 |  |  | 			{"semChan", newSemChan(c.cap)}, | 
					
						
							|  |  |  | 		} { | 
					
						
							|  |  |  | 			b.Run(fmt.Sprintf("%s-acquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) { | 
					
						
							|  |  |  | 				acquireN(b, w.w, c.size, c.N) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			b.Run(fmt.Sprintf("%s-tryAcquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) { | 
					
						
							|  |  |  | 				tryAcquireN(b, w.w, c.size, c.N) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |