Created
November 8, 2018 09:44
-
-
Save quasilyte/009edaf14aad08f6d1997b026c63c0a0 to your computer and use it in GitHub Desktop.
Channel vs mutex vs atomic for synchronized counter
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 benchmark | |
import ( | |
"context" | |
"runtime" | |
"sync" | |
"sync/atomic" | |
"testing" | |
"time" | |
) | |
// Скопируйте в любую (желательно пустую) директорию и запустите: | |
// $ go test -v -bench=. . | |
// Пример вывода: | |
// goos: linux | |
// goarch: amd64 | |
// BenchmarkChannel-8 3000000 429 ns/op | |
// BenchmarkMutex-8 10000000 204 ns/op | |
// BenchmarkAtomic-8 10000000 165 ns/op | |
// Значение, которое будет редактироваться из N горутин. | |
var sharedValue int64 | |
var valueMutex sync.Mutex | |
func addAtomic(x int64) { | |
atomic.AddInt64(&sharedValue, x) | |
} | |
// Функция, которая будет заниматься атомарным обновлением | |
// через операцию send+receive. Только одна горутина обновляет глобальную | |
// переменную - та, что держит канал на recveive. | |
func channelUpdater(ch <-chan int64) { | |
for x := range ch { | |
sharedValue += x | |
} | |
} | |
func addWithLocking(x int64) { | |
valueMutex.Lock() | |
sharedValue += x | |
valueMutex.Unlock() | |
} | |
func runBenchmark(b *testing.B, fn func()) { | |
sharedValue = 0 | |
ctx, cancel := context.WithCancel(context.Background()) | |
var wg sync.WaitGroup | |
wg.Add(runtime.NumCPU()) | |
for i := 0; i < runtime.NumCPU(); i++ { | |
go func() { | |
for { | |
select { | |
case <-ctx.Done(): | |
wg.Done() | |
return | |
default: | |
fn() | |
} | |
} | |
}() | |
} | |
for int(sharedValue) <= b.N { | |
time.Sleep(100) | |
} | |
cancel() | |
wg.Wait() | |
} | |
func BenchmarkChannel(b *testing.B) { | |
ch := make(chan int64) | |
go channelUpdater(ch) | |
var sendOnly chan<- int64 = ch | |
runBenchmark(b, func() { | |
sendOnly <- 1 | |
}) | |
close(ch) | |
} | |
func BenchmarkMutex(b *testing.B) { | |
runBenchmark(b, func() { | |
addWithLocking(1) | |
}) | |
} | |
func BenchmarkAtomic(b *testing.B) { | |
runBenchmark(b, func() { | |
addAtomic(1) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment