这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。这里复习一下之前的性能优化课程中老师所讲的内容。
一、slice 切片预分配内存:
切片有两个值,容量与长度。当长度超过容量时会触发自动扩容机制,如 容量为3扩容至容量6,但是并不是简单的两倍关系,具体细节可以查看这个帖子,写得还不错: www.cnblogs.com/sunshineliu…
预分配内存也就是说尽可能的估计所需的内存,来尽可能少的触发自动扩容机制。
测试代码如下:
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数组,内部维护一个内存扩容策略,不需要每次拼接重新分配内存。
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包替代加锁等