高质量编程与性能调优实战

简介:

  • 性能优化的前提是满足正确可靠,简洁清晰等质量因素
  • 性能优化是综合评估,有时候时间效率和空间效率可能对立

性能优化建议–Benchmark

go语言提供了支持基准性能测试,如何使用得去查一查

slice

  • slice预分配内存,尽可能在使用make()初始化切片时提供容量信息

    data := make([]int,0)
    data := make([]int,0,10)
    
  • 切片的底层实现就是可扩容的链表

  • 陷阱:大内存未释放

    1. 在已有的切片上创建切片,不会创建新的底层数组
    2. 原切片较大,代码在原切片的基础上新建小切片
    3. 原底层数组在内存中引用,得不到释放
  • 可使用copy替代re-slice

    func GetLastBySlice(origin []int) []int{
          
          
    	return origin[len(origin) - 2:]
    }
    
    // 下面这个节省的空间更多
    fun getLastByCopy(origin []int) []int{
          
          
    	result := make([]int,2)
    	copy(result,origin[len(origin) - 2:])
    	return result
    }
    

map

  • map预分配内存
    • 不断地向map中添加新元素地操作会触发map地扩容
    • 提前分配好空间可以减少内存拷贝和Rehash地消耗
    • 建议提前根据实际需求预估好需要的空间大小

strings.Buider进行字符串处理

  • 使用+拼接性能最差,strings.Buider,bytes.Buffer相近,strings。Buffer更快
  • 字符串在go里面时不可变类型,占用大小固定
  • 使用+每次都会重新分配内存
  • strings.Buider和bytes.Buffer底层都是使用[]byte数组

使用空结构体节省内存

扫描二维码关注公众号,回复: 16060368 查看本文章

使用atomic包

  • 锁的实现是通过操作系统来实现,属于系统调用, 调用成本很高

  • atomic操作是通过硬件实现,效率比锁高

  • sync.Mutex应该用来保护一段逻辑,。不仅仅用于保护一个变量

  • 对于非值操作,可以使用atomic.Value,能承载一个interface{}

    type atomicCounter struct{
          
          
        i int32
    }
    func AtomicAddOne(c *atomicCounter){
          
          
        atomic.AddInt32(&c.i,1)
    }
    
    type mutexCounter struct{
          
          
        i int32
        m sync.Mutex
    }
    func MutexAddOne(c *mutexCounter){
          
          
        c.m.Lock()
        c.i++
        c.m.UnLock()
    }
    
    
    

小结:

  • 避免常见的性能陷阱可以保证大部分程序的功能
  • 普通应用代码,不要一味地追求程序的性能
  • 越高级的性能优化手段越容易出现问题
  • 在满足正确可靠,简洁清晰的质量要求的前提下提高程序性能

猜你喜欢

转载自blog.csdn.net/weixin_68798281/article/details/131997060