Created
January 12, 2026 00:34
-
-
Save arunlakshman/2cedd03cd9513f7c80d986abb4355cb5 to your computer and use it in GitHub Desktop.
mutex-atomic-benchmark
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 main | |
| import ( | |
| "fmt" | |
| "runtime" | |
| "strings" | |
| "sync" | |
| "sync/atomic" | |
| "time" | |
| ) | |
| // MutexCounter uses a mutex to protect an integer counter | |
| type MutexCounter struct { | |
| mu sync.Mutex | |
| value int64 | |
| } | |
| func (c *MutexCounter) Increment() { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| c.value++ | |
| } | |
| func (c *MutexCounter) Get() int64 { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| return c.value | |
| } | |
| // AtomicCounter uses atomic operations for thread-safe integer operations | |
| type AtomicCounter struct { | |
| value int64 | |
| } | |
| func (c *AtomicCounter) Increment() { | |
| atomic.AddInt64(&c.value, 1) | |
| } | |
| func (c *AtomicCounter) Get() int64 { | |
| return atomic.LoadInt64(&c.value) | |
| } | |
| // BenchmarkConfig holds configuration for benchmarks | |
| type BenchmarkConfig struct { | |
| NumGoroutines int | |
| NumOperations int | |
| WarmupRuns int | |
| BenchmarkRuns int | |
| } | |
| // BenchmarkResult holds the results of a benchmark run | |
| type BenchmarkResult struct { | |
| Name string | |
| TotalOps int64 | |
| Duration time.Duration | |
| OpsPerSecond float64 | |
| AvgTimePerOp time.Duration | |
| FinalValue int64 | |
| NumGoroutines int | |
| } | |
| func runMutexBenchmark(config BenchmarkConfig) BenchmarkResult { | |
| var wg sync.WaitGroup | |
| counter := &MutexCounter{} | |
| start := time.Now() | |
| // Run operations | |
| for i := 0; i < config.NumGoroutines; i++ { | |
| wg.Add(1) | |
| go func() { | |
| defer wg.Done() | |
| for j := 0; j < config.NumOperations; j++ { | |
| counter.Increment() | |
| } | |
| }() | |
| } | |
| wg.Wait() | |
| duration := time.Since(start) | |
| totalOps := int64(config.NumGoroutines * config.NumOperations) | |
| opsPerSecond := float64(totalOps) / duration.Seconds() | |
| return BenchmarkResult{ | |
| Name: "Mutex", | |
| TotalOps: totalOps, | |
| Duration: duration, | |
| OpsPerSecond: opsPerSecond, | |
| AvgTimePerOp: duration / time.Duration(totalOps), | |
| FinalValue: counter.Get(), | |
| NumGoroutines: config.NumGoroutines, | |
| } | |
| } | |
| func runAtomicBenchmark(config BenchmarkConfig) BenchmarkResult { | |
| var wg sync.WaitGroup | |
| counter := &AtomicCounter{} | |
| start := time.Now() | |
| // Run operations | |
| for i := 0; i < config.NumGoroutines; i++ { | |
| wg.Add(1) | |
| go func() { | |
| defer wg.Done() | |
| for j := 0; j < config.NumOperations; j++ { | |
| counter.Increment() | |
| } | |
| }() | |
| } | |
| wg.Wait() | |
| duration := time.Since(start) | |
| totalOps := int64(config.NumGoroutines * config.NumOperations) | |
| opsPerSecond := float64(totalOps) / duration.Seconds() | |
| return BenchmarkResult{ | |
| Name: "Atomic", | |
| TotalOps: totalOps, | |
| Duration: duration, | |
| OpsPerSecond: opsPerSecond, | |
| AvgTimePerOp: duration / time.Duration(totalOps), | |
| FinalValue: counter.Get(), | |
| NumGoroutines: config.NumGoroutines, | |
| } | |
| } | |
| func printResults(results []BenchmarkResult) { | |
| fmt.Println("\n" + strings.Repeat("=", 80)) | |
| fmt.Printf("%-15s | %-12s | %-15s | %-18s | %-15s | %-12s\n", | |
| "Method", "Goroutines", "Total Ops", "Duration", "Ops/Second", "Final Value") | |
| fmt.Println(strings.Repeat("-", 80)) | |
| for _, result := range results { | |
| fmt.Printf("%-15s | %-12d | %-15d | %-18s | %-15.0f | %-12d\n", | |
| result.Name, | |
| result.NumGoroutines, | |
| result.TotalOps, | |
| result.Duration.Round(time.Microsecond), | |
| result.OpsPerSecond, | |
| result.FinalValue) | |
| } | |
| fmt.Println(strings.Repeat("=", 80)) | |
| } | |
| func printComparison(mutexResult, atomicResult BenchmarkResult) { | |
| fmt.Println("\n" + strings.Repeat("=", 80)) | |
| fmt.Println("PERFORMANCE COMPARISON") | |
| fmt.Println(strings.Repeat("=", 80)) | |
| speedup := mutexResult.Duration.Seconds() / atomicResult.Duration.Seconds() | |
| fmt.Printf("Atomic is %.2fx faster than Mutex\n", speedup) | |
| fmt.Printf("Mutex Duration: %v\n", mutexResult.Duration.Round(time.Microsecond)) | |
| fmt.Printf("Atomic Duration: %v\n", atomicResult.Duration.Round(time.Microsecond)) | |
| fmt.Printf("Time Saved: %v\n", (mutexResult.Duration - atomicResult.Duration).Round(time.Microsecond)) | |
| fmt.Printf("\nMutex Ops/Second: %.0f\n", mutexResult.OpsPerSecond) | |
| fmt.Printf("Atomic Ops/Second: %.0f\n", atomicResult.OpsPerSecond) | |
| fmt.Printf("Difference: %.0f ops/sec (%.1f%% improvement)\n", | |
| atomicResult.OpsPerSecond-mutexResult.OpsPerSecond, | |
| ((atomicResult.OpsPerSecond-mutexResult.OpsPerSecond)/mutexResult.OpsPerSecond)*100) | |
| fmt.Println(strings.Repeat("=", 80)) | |
| } | |
| func main() { | |
| fmt.Println("Mutex vs Atomic Integer Benchmark") | |
| fmt.Println("==================================") | |
| fmt.Printf("CPU Cores: %d\n", runtime.NumCPU()) | |
| fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0)) | |
| // Benchmark configurations | |
| configs := []BenchmarkConfig{ | |
| {NumGoroutines: 1, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 2, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 4, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 8, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 16, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 32, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| {NumGoroutines: 64, NumOperations: 1000000, WarmupRuns: 2, BenchmarkRuns: 5}, | |
| } | |
| var allResults []BenchmarkResult | |
| for _, config := range configs { | |
| fmt.Printf("\nRunning benchmark with %d goroutines, %d operations per goroutine...\n", | |
| config.NumGoroutines, config.NumOperations) | |
| // Warmup runs | |
| for i := 0; i < config.WarmupRuns; i++ { | |
| runMutexBenchmark(config) | |
| runAtomicBenchmark(config) | |
| } | |
| // Actual benchmark runs - average the results | |
| var mutexDurations []time.Duration | |
| var atomicDurations []time.Duration | |
| var mutexFinalValue int64 | |
| var atomicFinalValue int64 | |
| for i := 0; i < config.BenchmarkRuns; i++ { | |
| mutexResult := runMutexBenchmark(config) | |
| atomicResult := runAtomicBenchmark(config) | |
| mutexDurations = append(mutexDurations, mutexResult.Duration) | |
| atomicDurations = append(atomicDurations, atomicResult.Duration) | |
| mutexFinalValue = mutexResult.FinalValue | |
| atomicFinalValue = atomicResult.FinalValue | |
| } | |
| // Calculate averages | |
| var mutexAvgDuration, atomicAvgDuration time.Duration | |
| for _, d := range mutexDurations { | |
| mutexAvgDuration += d | |
| } | |
| for _, d := range atomicDurations { | |
| atomicAvgDuration += d | |
| } | |
| mutexAvgDuration /= time.Duration(len(mutexDurations)) | |
| atomicAvgDuration /= time.Duration(len(atomicDurations)) | |
| totalOps := int64(config.NumGoroutines * config.NumOperations) | |
| mutexResult := BenchmarkResult{ | |
| Name: "Mutex", | |
| TotalOps: totalOps, | |
| Duration: mutexAvgDuration, | |
| OpsPerSecond: float64(totalOps) / mutexAvgDuration.Seconds(), | |
| AvgTimePerOp: mutexAvgDuration / time.Duration(totalOps), | |
| FinalValue: mutexFinalValue, | |
| NumGoroutines: config.NumGoroutines, | |
| } | |
| atomicResult := BenchmarkResult{ | |
| Name: "Atomic", | |
| TotalOps: totalOps, | |
| Duration: atomicAvgDuration, | |
| OpsPerSecond: float64(totalOps) / atomicAvgDuration.Seconds(), | |
| AvgTimePerOp: atomicAvgDuration / time.Duration(totalOps), | |
| FinalValue: atomicFinalValue, | |
| NumGoroutines: config.NumGoroutines, | |
| } | |
| allResults = append(allResults, mutexResult, atomicResult) | |
| // Print comparison for this configuration | |
| printComparison(mutexResult, atomicResult) | |
| } | |
| // Print summary table | |
| printResults(allResults) | |
| fmt.Println("\nBenchmark completed!") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment