atomic包提供了两大类原子操作接口:一类是针对整型变量的,包括有符号整型、无符号整型以及对应的指针类型;另一个类是针对自定义类型的。
atomic包十分适合一些对性能十分敏感、并发量较大且读多写少的场合。如果要对一个复杂的临界区数据进行同步,那么首选依旧是sync包中的原语。
36.1 atomic包与原子作
atomic包是Go语言提供的原子操作原语相关的接口。
原子操作是相对于普通指令操作而言的,以一个整型变量自增的语句为例:
var a int
a++
a++
这行语句需要以下3条普通机器指令来完成变量a的自增:
- LOAD:将变量从内存加载到CPU寄存器
- ADD:执行加法指令
- STORE:将结果存储回原内存地址
这3条普通指令在执行过程中是可中断的,而原子操作的指令是不可中断的,和事务类似。
原子操作由底层硬件直接提供,atomic包封装了CPU实现的部分原子操作指令,因此atomic包提供的原语更接近硬件底层,也更为低级,它常被用于实现更为高级的并发同步技术(如channel和sync包的同步原语)。
以atomic.SwapInt64函数在x86_64平台上的实现为例,它基本上就是对x86_64 CPU实现的原子操作指令XCHGQ的直接封装。
36.2 对共享整型变量的无锁读写
- 利用原子操作的无锁并发写的性能随着并发量增大几乎保持恒定
- 利用原子操作的无锁并发读的性能随着并发量增加有持续提升的趋势,并且性能约为读锁的200倍
var n1 int64
func addSyncByAtomic(delta int64) int64 {
return atomic.AddInt64(&n1, delta)
}
func readSyncByAtomic() int64 {
return atomic.LoadInt64(&n1)
}
var n2 int64
var rwmu sync.RWMutex
func addSyncByRWMutex(delta int64) {
rwmu.Lock()
n2 += delta
rwmu.Unlock()
}
func readSyncByRWMutex() int64 {
var n int64
rwmu.RLock()
n = n2
rwmu.RUnlock()
return n
}
func BenchmarkAddSyncByAtomic(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
addSyncByAtomic(1)
}
})
}
func BenchmarkReadSyncByAtomic(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
readSyncByAtomic()
}
})
}
func BenchmarkAddSyncByRWMutex(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
addSyncByRWMutex(1)
}
})
}
func BenchmarkReadSyncByRWMutex(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
readSyncByRWMutex()
}
})
}
36.3 对共享自定义类型变量的无锁读写
atomic通过Value类型的装拆箱操作实现了对任意自定义类型的原子操作(Load和Store),从而实现对共享自定义类型变量无锁读写的支持
- 利用原子操作的无锁并发写的性能随着并发量增大而小幅下降
- 利用原子操作的无锁并发读的性能随着并发量增加有持续提升的趋势,并且性能约为读锁的100倍
type Config struct {
sync.RWMutex
data string
}
func BenchmarkRWMutexSet(b *testing.B) {
config := Config{}
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
config.Lock()
config.data = "hello"
config.Unlock()
}
})
}
func BenchmarkRWMutexGet(b *testing.B) {
config := Config{data: "hello"}
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
config.RLock()
_ = config.data
config.RUnlock()
}
})
}
func BenchmarkAtomicSet(b *testing.B) {
var config atomic.Value
c := Config{data: "hello"}
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
config.Store(c)
}
})
}
func BenchmarkAtomicGet(b *testing.B) {
var config atomic.Value
config.Store(Config{data: "hello"})
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = config.Load().(Config)
}
})
}
标签:读书笔记,testing,36,pb,并发,atomic,func,config
From: https://www.cnblogs.com/brynchen/p/18032201