关于Go语言的一些性能优化

211 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 18 天,点击查看活动详情 

尽量不用锁 RWMutex

sync.Map 适合 读多写少的情况

concurrentMap 写多读少的时候 减小锁的粒度

package lock

import (
   "strconv"
   "sync"
   "testing"
)


const (
   NumOfReader = 200
   NumOfWriter = 100
)


type Map interface {
   Set(key interface{}, val interface{})
   Get(key interface{}) (interface{}, bool)
   Del(key interface{})
}

func benchmarkMap(b *testing.B, hm Map) {
   for i:=0;i<b.N;i++{
      var wg sync.WaitGroup
      for i:=0;i<NumOfWriter;i++{
         wg.Add(1)
         go func() {
            for i:=0;i<100;i++{
               hm.Set(strconv.Itoa(i),i*i)
               hm.Set(strconv.Itoa(i),i*i)
               hm.Del(strconv.Itoa(i))
            }
            wg.Done()
         }()
      }
      for i:=0;i<NumOfReader;i++{
         wg.Add(1)
         go func() {
            for i:=0;i<100;i++{
               hm.Get(strconv.Itoa(i))
            }
            wg.Done()
         }()
      }
      wg.Wait()
   }
}

func BenchmarkSyncMap(b *testing.B)  {
   b.Run("map with RWLock", func(b *testing.B) {
      hm:=CreateRWLockMap()
      benchmarkMap(b,hm)
   })

   b.Run("map with SyncMap", func(b *testing.B) {
      hm:=CreateSyncMapBenchMarkAdapter()
      benchmarkMap(b,hm)
   })

   b.Run("map with ConcureentMap", func(b *testing.B) {
      hm:=CreateConcureentMapBenchmarkAdapter(199)
      benchmarkMap(b,hm)
   })
}

NumOfReader = 100
NumOfWriter = 100

命名同样时间执行多少次一次执行了多少时间
BenchmarkSyncMap/map_with_RWLock-81826618349 ns/op
BenchmarkSyncMap/map_with_SyncMap-810011801721 ns/op
BenchmarkSyncMap/map_with_ConcureentMap-84422653011 ns/op

NumOfReader = 100
NumOfWriter = 200

命名同样时间执行多少次一次执行了多少时间
BenchmarkSyncMap/map_with_RWLock-88613309623 ns/op
BenchmarkSyncMap/map_with_SyncMap-86020316582 ns/op
BenchmarkSyncMap/map_with_ConcureentMap-82714765438 ns/op

NumOfReader = 200
NumOfWriter = 100

命名同样时间执行多少次一次执行了多少时间
BenchmarkSyncMap/map_with_RWLock-81577036303 ns/op
BenchmarkSyncMap/map_with_SyncMap-810011625267 ns/op
BenchmarkSyncMap/map_with_ConcureentMap-83423173536 ns/op

其实我们可以看出,关于锁的方面。sync.Map并不是无敌,而是适合读多写少的情况。引入一个新的github.com/easierway/concurrent_map包,读多写多性能都很好,因为这个包减小了锁的粒度。

另一些性能优化的例子

  1. 字符串拼接使用string.Builder来优化

  2. json的序列化与反序列化使用 easyjson 来优化

  3. 引用类型 尽量传送 引用 而不是 传值

  4. 切片应当在初始状态下定义好预定容量,过大占用内存空间,过小开辟内存引起消耗

  5. 可以内存复用,例如使用sync.Once 来Do一次,来减少性能的开销。

  6. sync.Pool 使用通过复用,降低对象的创建和GC的代价,是线程安全的,但是有大锁,这也是导致性能不是很好。另外生命周期受到GC影响,不适合做连接池,需要自己管理生命周期资源的池化。所以链接池最好还是用缓冲的chan来做

  7. 尽量不使用反射,例如 json包中序列化与反序列化就用到了反射,fmt.Sprintf()中也用到了反射,所以两者性能并不理想。