持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
我不确定大家是否看过这个表格
没看过可以考虑收藏
操作 | 延迟 |
---|---|
执行一个指令 | 1 ns |
L1 缓存查询 | 0.5 ns |
分支预测错误(Branch mispredict) | 3 ns |
L2 缓存查询 | 4 ns |
互斥锁/解锁 | 17 ns |
在 1Gbps 的网络上发送 2KB | 44 ns |
主存访问 | 100 ns |
Zippy 压缩 1KB | 2,000 ns |
从内存顺序读取 1 MB | 3,000 ns |
SSD 随机读 | 16,000 ns |
从 SSD 顺序读取 1 MB | 49,000 ns |
同一个数据中心往返 | 500,000 ns |
从磁盘顺序读取 1 MB | 825,000 ns |
磁盘寻址 | 2,000,000 ns (2 ms) |
从美国发送到欧洲的数据包 | 150,000,000 ns(150 ms) |
但有的时候仅仅了解这些概念是不够的
由于很多时候我们的抉择并不是在网络传输、内存、硬盘中选择,我们当然知道内存大于硬盘。本文主要是为了探讨go语言中通道、原子操作、锁的耗时,那么首先我们需要定义耗时,我们定义了两个协程消费资源,在消费10000个资源后记录耗时
测试代码如下:
// 通道
func Bgo(ch chan int, cnt chan int) {
for {
select {
case a := <-ch:
ch <- a + 1
if a >= 10000 {
cnt <- a + 1
return
}
case <-time.After(1 * time.Second):
cnt <- 7777
return
}
}
}
// 原子操作
func Cgo(count *int64) {
for {
atomic.AddInt64(count, 1)
if *count > 10000 {
return
}
}
}
// 锁
func Dgo(count *int64) {
defer lock.Unlock()
for {
lock.Lock()
*count = *count + 1
if *count > 10000 {
return
}
lock.Unlock()
}
}
复制代码
记录耗时:
start = time.Now().UnixMicro()
ch <- 1
go Bgo(ch, cnt)
go Bgo(ch, cnt)
fmt.Println(<-cnt)
fmt.Println(time.Now().UnixMicro() - start)
// 两个协程原子操作
start = time.Now().UnixMicro()
var count int64 = 1
go Cgo(&count)
Cgo(&count)
fmt.Println(time.Now().UnixMicro() - start + count - 10001)
fmt.Println(count)
// 两个协程Mutex
start = time.Now().UnixNano()
var count1 int64 = 1
go Dgo(&count1)
Dgo(&count1)
fmt.Println(time.Now().UnixNano() - start + count1 - 10001)
fmt.Println(count1)
复制代码
结论
从结论上看,通道的耗时最高,原子操作为微秒级,锁波动比较大有的时候纳秒级也无法计数。
这也是发这篇文章的一个原因,我印象里锁本身耗时应该并没有这么少。希望掘友们指正。