Using a mutex to protect a single int operation is quite heavyweight.
Using sync/atomic provides much better performance. This change was
benchmarked as such:
BenchmarkSync
10000000 139 ns/op
BenchmarkAtomic
200000000 9.90 ns/op
package blah
import (
"sync"
"sync/atomic"
"testing"
)
type Int struct {
mu sync.RWMutex
i int64
}
func (v *Int) Add(delta int64) {
v.mu.Lock()
defer v.mu.Unlock()
v.i += delta
}
type AtomicInt struct {
i int64
}
func (v *AtomicInt) Add(delta int64) {
atomic.AddInt64(&v.i, delta)
}
func BenchmarkSync(b *testing.B) {
s := new(Int)
for i := 0; i < b.N; i++ {
s.Add(1)
}
}
func BenchmarkAtomic(b *testing.B) {
s := new(AtomicInt)
for i := 0; i < b.N; i++ {
s.Add(1)
}
}
Change-Id: I6998239c785967647351bbfe8533c38e4894543b
Reviewed-on: https://go-review.googlesource.com/3430
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
"sort"
"strconv"
"sync"
+ "sync/atomic"
)
// Var is an abstract type for all exported variables.
// Int is a 64-bit integer variable that satisfies the Var interface.
type Int struct {
- mu sync.RWMutex
- i int64
+ i int64
}
func (v *Int) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.FormatInt(v.i, 10)
+ return strconv.FormatInt(atomic.LoadInt64(&v.i), 10)
}
func (v *Int) Add(delta int64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.i += delta
+ atomic.AddInt64(&v.i, delta)
}
func (v *Int) Set(value int64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.i = value
+ atomic.StoreInt64(&v.i, value)
}
// Float is a 64-bit float variable that satisfies the Var interface.