性能优化指南 | 青训营笔记

89 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。这里复习一下之前的性能优化课程中老师所讲的内容。

一、slice 切片预分配内存:

切片有两个值,容量与长度。当长度超过容量时会触发自动扩容机制,如 容量为3扩容至容量6,但是并不是简单的两倍关系,具体细节可以查看这个帖子,写得还不错: www.cnblogs.com/sunshineliu…

预分配内存也就是说尽可能的估计所需的内存,来尽可能少的触发自动扩容机制。

image.png

测试代码如下:

func main() {
   size := 100000000
   a := make([]int, 0, size)
   start_time := time.Now()
   for i := 0; i < size; i++ {
      a = append(a, i)
   }
   spend_time := time.Since(start_time)
   fmt.Println("预分配内存花费时间: ", spend_time)

   b := make([]int, 0)
   start_time = time.Now()
   for i := 0; i < size; i++ {
      b = append(b, i)
   }
   spend_time = time.Since(start_time)
   fmt.Println("未预分配内存花费时间: ", spend_time)
}

其次,视频中指出,要警惕大内存未释放的情况,也就是说一个切片引用了一个大数组,另一个切片在此切片的基础上再次创建切片,这种情况仍然引用的还是那个底层大数组。这种情况,建议用copy函数(此时将origin[5:]移入res中):

copy(res, origin[5:])

二、map预分配内存 略

三、字符串处理时,使用strings.builder。字符串每次拼接时都会重新开辟新的内存。 strings.builder效果略好于byte.buffer,它们底层都是[]byte数组,内部维护一个内存扩容策略,不需要每次拼接重新分配内存。

image.png

   size := 1000000  //这里循环次数较多,可以减少一些
   str := "hello world!"
   var temp string
   start_time := time.Now()
   for i := 0; i < size; i++ {
      temp += str
   }
   spend_time := time.Since(start_time)
   fmt.Println("直接拼接花费时间: ", spend_time)

   var strbuilder strings.Builder
   start_time = time.Now()
   for i := 0; i < size; i++ {
      strbuilder.WriteString(str)
   }
   spend_time = time.Since(start_time)
   fmt.Println("使用strings.builder未预分配内存花费时间: ", spend_time)

   var strbuilder2 strings.Builder
   strbuilder2.Grow(size * len(str))
   start_time = time.Now()
   for i := 0; i < size; i++ {
      strbuilder2.WriteString(str)
   }
   spend_time = time.Since(start_time)
   fmt.Println("使用strings.builder预分配内存花费时间: ", spend_time)
}

四、使用空结构体作为占位符节省内存: make(map[int]struct{}) 尤其是实现集合(set)时,因为我们只需要用到键,而不需要值。

五、使用atomic包: 使用atomic包替代加锁等

image.png