写在性能优化之前——了解些耗时概念

持续创作,加速成长!这是我参与「掘金日新计划 · 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)
复制代码

结论

从结论上看,通道的耗时最高,原子操作为微秒级,锁波动比较大有的时候纳秒级也无法计数。

这也是发这篇文章的一个原因,我印象里锁本身耗时应该并没有这么少。希望掘友们指正。

猜你喜欢

转载自juejin.im/post/7101686337111228452