1
0
mirror of https://github.com/golang/go synced 2024-11-26 04:27:58 -07:00

runtime: add direct benchmark of mutex contention

Measure throughput of a single mutex with all threads contending. Do not
attempt to measure fairness/starvation.

The ChanContended benchmark works somewhat well for this (interacting
with the mutex is a large contributor to its results), but it's better
to be clear about what we're attempting to measure.

For #68578

Change-Id: Ie397b4c363bfcd5afddf796a81cd6c34ebf8551b
Reviewed-on: https://go-review.googlesource.com/c/go/+/604375
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Rhys Hiltner <rhys.hiltner@gmail.com>
This commit is contained in:
Rhys Hiltner 2024-08-02 15:31:22 -07:00 committed by Gopher Robot
parent 820d445876
commit c8ccbcdde5

View File

@ -564,6 +564,48 @@ func BenchmarkOSYield(b *testing.B) {
} }
} }
func BenchmarkMutexContention(b *testing.B) {
// Measure throughput of a single mutex with all threads contending
//
// Share a single counter across all threads. Progress from any thread is
// progress for the benchmark as a whole. We don't measure or give points
// for fairness here, arbitrary delay to any given thread's progress is
// invisible and allowed.
//
// The cache line that holds the count value will need to move between
// processors, but not as often as the cache line that holds the mutex. The
// mutex protects access to the count value, which limits contention on that
// cache line. This is a simple design, but it helps to make the behavior of
// the benchmark clear. Most real uses of mutex will protect some number of
// cache lines anyway.
var state struct {
_ cpu.CacheLinePad
lock Mutex
_ cpu.CacheLinePad
count atomic.Int64
_ cpu.CacheLinePad
}
procs := GOMAXPROCS(0)
var wg sync.WaitGroup
for range procs {
wg.Add(1)
go func() {
defer wg.Done()
for {
Lock(&state.lock)
ours := state.count.Add(1)
Unlock(&state.lock)
if ours >= int64(b.N) {
return
}
}
}()
}
wg.Wait()
}
func BenchmarkMutexHandoff(b *testing.B) { func BenchmarkMutexHandoff(b *testing.B) {
testcase := func(delay func(l *Mutex)) func(b *testing.B) { testcase := func(delay func(l *Mutex)) func(b *testing.B) {
return func(b *testing.B) { return func(b *testing.B) {
@ -590,11 +632,11 @@ func BenchmarkMutexHandoff(b *testing.B) {
// each other in a non-blocking way via the "turn" state. // each other in a non-blocking way via the "turn" state.
var state struct { var state struct {
_ [cpu.CacheLinePadSize]byte _ cpu.CacheLinePad
lock Mutex lock Mutex
_ [cpu.CacheLinePadSize]byte _ cpu.CacheLinePad
turn atomic.Int64 turn atomic.Int64
_ [cpu.CacheLinePadSize]byte _ cpu.CacheLinePad
} }
var delta atomic.Int64 var delta atomic.Int64