diff options
author | EuAndreh <eu@euandre.org> | 2025-01-23 09:54:11 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-01-23 11:36:44 -0300 |
commit | 5f6607152a96b85f1518028b8f1ee1ef1aefda75 (patch) | |
tree | be54946ddb2510a317322a2660563c83cab2117c /tests/stm.go | |
parent | tests/stm.go: Turn example into functional test (diff) | |
download | stm-5f6607152a96b85f1518028b8f1ee1ef1aefda75.tar.gz stm-5f6607152a96b85f1518028b8f1ee1ef1aefda75.tar.xz |
Move benchmarks from tests/stm.go to tests/benchmarks/*
Diffstat (limited to 'tests/stm.go')
-rw-r--r-- | tests/stm.go | 405 |
1 files changed, 18 insertions, 387 deletions
diff --git a/tests/stm.go b/tests/stm.go index 49fcd25..13b375a 100644 --- a/tests/stm.go +++ b/tests/stm.go @@ -2,7 +2,6 @@ package stm import ( "context" - "fmt" "math" "runtime" "sync" @@ -15,6 +14,17 @@ import ( +const ( + d = 100 * time.Millisecond +) + + +var ( + t0 = time.Now() +) + + + func TestValue(t *testing.T) { v := New("hello") g.TAssertEqual("hello", v.Load()) @@ -41,332 +51,6 @@ func TestInt32(t *testing.T) { g.TAssertEqual(false, v.CompareAndSwap(0, 10)) } -func BenchmarkInt64Add(b *testing.B) { - v := NewInt64(0) - for i := 0; i < b.N; i++ { - v.Add(1) - } -} - -func BenchmarkIntInterfaceAdd(b *testing.B) { - var v Int[int64] = NewInt64(0) - for i := 0; i < b.N; i++ { - v.Add(1) - } -} - -func BenchmarkStdlibInt64Add(b *testing.B) { - var n int64 - for i := 0; i < b.N; i++ { - atomic.AddInt64(&n, 1) - } -} - -func BenchmarkInterfaceStore(b *testing.B) { - var v Interface[string] = New("hello") - for i := 0; i < b.N; i++ { - v.Store(fmt.Sprint(i)) - } -} - -func BenchmarkValueStore(b *testing.B) { - v := New("hello") - for i := 0; i < b.N; i++ { - v.Store(fmt.Sprint(i)) - } -} - -func BenchmarkStdlibValueStore(b *testing.B) { - v := atomic.Value{} - for i := 0; i < b.N; i++ { - v.Store(fmt.Sprint(i)) - } -} - -func BenchmarkDeref(b *testing.B) { - x := NewVar(0) - for i := 0; i < b.N; i++ { - Deref(x) - } -} - -func BenchmarkAtomicSet(b *testing.B) { - x := NewVar(0) - for i := 0; i < b.N; i++ { - AtomicSet(x, 0) - } -} - -func BenchmarkIncrementSTM(b *testing.B) { - for i := 0; i < b.N; i++ { - // spawn 1000 goroutines that each increment x by 1 - x := NewVar(0) - for i := 0; i < 1000; i++ { - go Atomically(VoidOperation(func(tx *Tx) { - cur := x.Get(tx) - x.Set(tx, cur+1) - })) - } - // wait for x to reach 1000 - Atomically(VoidOperation(func(tx *Tx) { - tx.Assert(x.Get(tx) == 1000) - })) - } -} - -func BenchmarkIncrementMutex(b *testing.B) { - for i := 0; i < b.N; i++ { - var mu sync.Mutex - x := 0 - for i := 0; i < 1000; i++ { - go func() { - mu.Lock() - x++ - mu.Unlock() - }() - } - for { - mu.Lock() - read := x - mu.Unlock() - if read == 1000 { - break - } - } - } -} - -func BenchmarkIncrementChannel(b *testing.B) { - for i := 0; i < b.N; i++ { - c := make(chan int, 1) - c <- 0 - for i := 0; i < 1000; i++ { - go func() { - c <- 1 + <-c - }() - } - for { - read := <-c - if read == 1000 { - break - } - c <- read - } - } -} - -func BenchmarkReadVarSTM(b *testing.B) { - for i := 0; i < b.N; i++ { - var wg sync.WaitGroup - wg.Add(1000) - x := NewVar(0) - for i := 0; i < 1000; i++ { - go func() { - Deref(x) - wg.Done() - }() - } - wg.Wait() - } -} - -func BenchmarkReadVarMutex(b *testing.B) { - for i := 0; i < b.N; i++ { - var mu sync.Mutex - var wg sync.WaitGroup - wg.Add(1000) - x := 0 - for i := 0; i < 1000; i++ { - go func() { - mu.Lock() - _ = x - mu.Unlock() - wg.Done() - }() - } - wg.Wait() - } -} - -func BenchmarkReadVarChannel(b *testing.B) { - for i := 0; i < b.N; i++ { - var wg sync.WaitGroup - wg.Add(1000) - c := make(chan int) - close(c) - for i := 0; i < 1000; i++ { - go func() { - <-c - wg.Done() - }() - } - wg.Wait() - } -} - -func parallelPingPongs(b *testing.B, n int) { - var wg sync.WaitGroup - wg.Add(n) - for i := 0; i < n; i++ { - go func() { - defer wg.Done() - testPingPong(b, b.N, func(string) {}) - }() - } - wg.Wait() -} - -func BenchmarkPingPong4(b *testing.B) { - b.ReportAllocs() - parallelPingPongs(b, 4) -} - -func BenchmarkPingPong(b *testing.B) { - b.ReportAllocs() - parallelPingPongs(b, 1) -} - -const maxTokens = 25 - -func BenchmarkThunderingHerdCondVar(b *testing.B) { - for i := 0; i < b.N; i++ { - var mu sync.Mutex - consumer := sync.NewCond(&mu) - generator := sync.NewCond(&mu) - done := false - tokens := 0 - var pending sync.WaitGroup - for i := 0; i < 1000; i++ { - pending.Add(1) - go func() { - mu.Lock() - for { - if tokens > 0 { - tokens-- - generator.Signal() - break - } - consumer.Wait() - } - mu.Unlock() - pending.Done() - }() - } - go func() { - mu.Lock() - for !done { - if tokens < maxTokens { - tokens++ - consumer.Signal() - } else { - generator.Wait() - } - } - mu.Unlock() - }() - pending.Wait() - mu.Lock() - done = true - generator.Signal() - mu.Unlock() - } - -} - -func BenchmarkThunderingHerd(b *testing.B) { - for i := 0; i < b.N; i++ { - done := NewBuiltinEqVar(false) - tokens := NewBuiltinEqVar(0) - pending := NewBuiltinEqVar(0) - for i := 0; i < 1000; i++ { - Atomically(VoidOperation(func(tx *Tx) { - pending.Set(tx, pending.Get(tx)+1) - })) - go func() { - Atomically(VoidOperation(func(tx *Tx) { - t := tokens.Get(tx) - if t > 0 { - tokens.Set(tx, t-1) - pending.Set(tx, pending.Get(tx)-1) - } else { - tx.Retry() - } - })) - }() - } - go func() { - for Atomically(func(tx *Tx) bool { - if done.Get(tx) { - return false - } - tx.Assert(tokens.Get(tx) < maxTokens) - tokens.Set(tx, tokens.Get(tx)+1) - return true - }) { - } - }() - Atomically(VoidOperation(func(tx *Tx) { - tx.Assert(pending.Get(tx) == 0) - })) - AtomicSet(done, true) - } -} - -func BenchmarkInvertedThunderingHerd(b *testing.B) { - for i := 0; i < b.N; i++ { - done := NewBuiltinEqVar(false) - tokens := NewBuiltinEqVar(0) - pending := NewVar(NewSet[*Var[bool]]()) - for i := 0; i < 1000; i++ { - ready := NewVar(false) - Atomically(VoidOperation(func(tx *Tx) { - pending.Set(tx, pending.Get(tx).Add(ready)) - })) - go func() { - Atomically(VoidOperation(func(tx *Tx) { - tx.Assert(ready.Get(tx)) - set := pending.Get(tx) - if !set.Contains(ready) { - panic("couldn't find ourselves in pending") - } - pending.Set(tx, set.Delete(ready)) - })) - //b.Log("waiter finished") - }() - } - go func() { - for Atomically(func(tx *Tx) bool { - if done.Get(tx) { - return false - } - tx.Assert(tokens.Get(tx) < maxTokens) - tokens.Set(tx, tokens.Get(tx)+1) - return true - }) { - } - }() - go func() { - for Atomically(func(tx *Tx) bool { - tx.Assert(tokens.Get(tx) > 0) - tokens.Set(tx, tokens.Get(tx)-1) - pending.Get(tx).Range(func(ready *Var[bool]) bool { - if !ready.Get(tx) { - ready.Set(tx, true) - return false - } - return true - }) - return !done.Get(tx) - }) { - } - }() - Atomically(VoidOperation(func(tx *Tx) { - tx.Assert(pending.Get(tx).(Lenner).Len() == 0) - })) - AtomicSet(done, true) - } -} - func TestLimit(t *testing.T) { if Limit(10) == Inf { t.Errorf("Limit(10) == Inf should be false") @@ -404,26 +88,6 @@ func TestEvery(t *testing.T) { } } -const ( - d = 100 * time.Millisecond -) - -var ( - t0 = time.Now() - t1 = t0.Add(time.Duration(1) * d) - t2 = t0.Add(time.Duration(2) * d) - t3 = t0.Add(time.Duration(3) * d) - t4 = t0.Add(time.Duration(4) * d) - t5 = t0.Add(time.Duration(5) * d) - t9 = t0.Add(time.Duration(9) * d) -) - -type allow struct { - t time.Time - n int - ok bool -} - // //func run(t *testing.T, lim *Limiter, allows []allow) { // for i, allow := range allows { @@ -576,25 +240,6 @@ func TestLongRunningQPS(t *testing.T) { } } -type request struct { - t time.Time - n int - act time.Time - ok bool -} - -// dFromDuration converts a duration to a multiple of the global constant d -func dFromDuration(dur time.Duration) int { - // Adding a millisecond to be swallowed by the integer division - // because we don't care about small inaccuracies - return int((dur + time.Millisecond) / d) -} - -// dSince returns multiples of d since t0 -func dSince(t time.Time) int { - return dFromDuration(t.Sub(t0)) -} - // //func runReserve(t *testing.T, lim *Limiter, req request) *Reservation { // return runReserveMax(t, lim, req, InfDuration) @@ -750,6 +395,13 @@ type wait struct { nilErr bool } +// dFromDuration converts a duration to a multiple of the global constant d +func dFromDuration(dur time.Duration) int { + // Adding a millisecond to be swallowed by the integer division + // because we don't care about small inaccuracies + return int((dur + time.Millisecond) / d) +} + func runWait(t *testing.T, lim *Limiter, w wait) { t.Helper() start := time.Now() @@ -808,27 +460,6 @@ func TestWaitInf(t *testing.T) { runWait(t, lim, wait{"exceed-burst-no-error", context.Background(), 3, 0, true}) } -func BenchmarkAllowN(b *testing.B) { - lim := NewLimiter(Every(1*time.Second), 1) - b.ReportAllocs() - b.ResetTimer() - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - lim.AllowN(1) - } - }) -} - -func BenchmarkWaitNNoDelay(b *testing.B) { - lim := NewLimiter(Limit(b.N), b.N) - ctx := context.Background() - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - lim.WaitN(ctx, 1) - } -} - func TestDecrement(t *testing.T) { x := NewVar(1000) for i := 0; i < 500; i++ { |