Created
October 6, 2025 22:23
-
-
Save emcfarlane/eab8ab5803fc927aa54693e51593f062 to your computer and use it in GitHub Desktop.
Benchmark leader election
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package go | |
| import ( | |
| "sync" | |
| "testing" | |
| ) | |
| func BenchmarkLeaderSyncMap(b *testing.B) { | |
| // Simulate work that a leader performs. | |
| doWork := func(key string) int { | |
| sum := 0 | |
| for i := 0; i < 100; i++ { | |
| sum += len(key) + i | |
| } | |
| return sum | |
| } | |
| b.Run("HighContention", func(b *testing.B) { | |
| // Many goroutines racing for the same key. | |
| var m sync.Map | |
| key := "shared-key" | |
| b.RunParallel(func(pb *testing.PB) { | |
| for pb.Next() { | |
| task := &taskSyncMap{cond: make(chan struct{})} | |
| actual, beaten := m.LoadOrStore(key, task) | |
| loaded := actual.(*taskSyncMap) | |
| if !beaten { | |
| // Leader: do work and broadcast. | |
| loaded.result = doWork(key) | |
| close(loaded.cond) | |
| } else { | |
| // Waiter: wait for leader to finish. | |
| <-loaded.cond | |
| } | |
| _ = loaded.result | |
| } | |
| }) | |
| }) | |
| b.Run("LowContention", func(b *testing.B) { | |
| // Each goroutine works on different keys. | |
| var m sync.Map | |
| b.RunParallel(func(pb *testing.PB) { | |
| i := 0 | |
| for pb.Next() { | |
| key := b.Name() + "-" + string(rune(i)) | |
| i++ | |
| task := &taskSyncMap{cond: make(chan struct{})} | |
| actual, beaten := m.LoadOrStore(key, task) | |
| loaded := actual.(*taskSyncMap) | |
| if !beaten { | |
| // Leader: do work and broadcast. | |
| loaded.result = doWork(key) | |
| close(loaded.cond) | |
| } else { | |
| // Waiter: wait for leader to finish. | |
| <-loaded.cond | |
| } | |
| _ = loaded.result | |
| } | |
| }) | |
| }) | |
| b.Run("MixedContention", func(b *testing.B) { | |
| // Multiple keys with some contention on each. | |
| var m sync.Map | |
| keys := []string{"key-1", "key-2", "key-3", "key-4"} | |
| b.RunParallel(func(pb *testing.PB) { | |
| i := 0 | |
| for pb.Next() { | |
| key := keys[i%len(keys)] | |
| i++ | |
| task := &taskSyncMap{cond: make(chan struct{})} | |
| actual, beaten := m.LoadOrStore(key, task) | |
| loaded := actual.(*taskSyncMap) | |
| if !beaten { | |
| // Leader: do work and broadcast. | |
| loaded.result = doWork(key) | |
| close(loaded.cond) | |
| } else { | |
| // Waiter: wait for leader to finish. | |
| <-loaded.cond | |
| } | |
| _ = loaded.result | |
| } | |
| }) | |
| }) | |
| } | |
| func BenchmarkLeaderLock(b *testing.B) { | |
| // Simulate work that a leader performs. | |
| doWork := func(key string) int { | |
| sum := 0 | |
| for i := 0; i < 100; i++ { | |
| sum += len(key) + i | |
| } | |
| return sum | |
| } | |
| b.Run("HighContention", func(b *testing.B) { | |
| // Many goroutines racing for the same key. | |
| var mu sync.Mutex | |
| m := make(map[string]*taskLock) | |
| key := "shared-key" | |
| b.RunParallel(func(pb *testing.PB) { | |
| for pb.Next() { | |
| mu.Lock() | |
| task, exists := m[key] | |
| if !exists { | |
| task = &taskLock{} | |
| task.wg.Add(1) | |
| m[key] = task | |
| mu.Unlock() | |
| // Leader: do work and signal completion. | |
| task.result = doWork(key) | |
| task.wg.Done() | |
| } else { | |
| mu.Unlock() | |
| // Waiter: wait for leader to finish. | |
| task.wg.Wait() | |
| } | |
| _ = task.result | |
| } | |
| }) | |
| }) | |
| b.Run("LowContention", func(b *testing.B) { | |
| // Each goroutine works on different keys. | |
| var mu sync.Mutex | |
| m := make(map[string]*taskLock) | |
| b.RunParallel(func(pb *testing.PB) { | |
| i := 0 | |
| for pb.Next() { | |
| key := b.Name() + "-" + string(rune(i)) | |
| i++ | |
| mu.Lock() | |
| task, exists := m[key] | |
| if !exists { | |
| task = &taskLock{} | |
| task.wg.Add(1) | |
| m[key] = task | |
| mu.Unlock() | |
| // Leader: do work and signal completion. | |
| task.result = doWork(key) | |
| task.wg.Done() | |
| } else { | |
| mu.Unlock() | |
| // Waiter: wait for leader to finish. | |
| task.wg.Wait() | |
| } | |
| _ = task.result | |
| } | |
| }) | |
| }) | |
| b.Run("MixedContention", func(b *testing.B) { | |
| // Multiple keys with some contention on each. | |
| var mu sync.Mutex | |
| m := make(map[string]*taskLock) | |
| keys := []string{"key-1", "key-2", "key-3", "key-4"} | |
| b.RunParallel(func(pb *testing.PB) { | |
| i := 0 | |
| for pb.Next() { | |
| key := keys[i%len(keys)] | |
| i++ | |
| mu.Lock() | |
| task, exists := m[key] | |
| if !exists { | |
| task = &taskLock{} | |
| task.wg.Add(1) | |
| m[key] = task | |
| mu.Unlock() | |
| // Leader: do work and signal completion. | |
| task.result = doWork(key) | |
| task.wg.Done() | |
| } else { | |
| mu.Unlock() | |
| // Waiter: wait for leader to finish. | |
| task.wg.Wait() | |
| } | |
| _ = task.result | |
| } | |
| }) | |
| }) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment