| // 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. |
| |
| //go:build go1.7 |
| // +build go1.7 |
| |
| package semaphore_test |
| |
| import ( |
| "context" |
| "fmt" |
| "testing" |
| |
| "golang.org/x/sync/semaphore" |
| ) |
| |
| // 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++ { |
| _ = semaphore.NewWeighted(cap) |
| } |
| }) |
| 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 |
| }{ |
| {"Weighted", semaphore.NewWeighted(c.cap)}, |
| {"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) |
| }) |
| } |
| } |
| } |