性能优化建议
简介
- 性能优化的前提是满足正确可靠、简介清晰等质量因素
- 性能优化是综合评估,有时候时间效率和空间效率可能对立
Benchmark
简介
- 基准测试(benchmarking)是一种测量和评估软件性能指标的活动(是一个评价方式)。在某个时候通过基准测试建立一个已知的性能水平(称为基准线)。其在整个计算机领域有着长期的应用,计算机领域应用最成功的就是性能测试。
使用
- go 语言提供了支持基准性能测试的benchmark工具
go test -bench=. -benchmem
结果说明
Slice预分配内存
-
尽可能在使用
make()初始化切片的时候提供容量信息- 性能方面时间比例接近是1:3
-
切片本质是一个数组片段的描述
- 数组指针
- 片段的长度
- 片段的容量(不改变内存分配情况下的最大长度)
-
切片操作并不会复制切片指向的元素
-
创建一个新的切片会复用原来切片的底层数组
陷阱:大内存未释放
-
在以后切片基础上创建切片,不会创建新的底层数组
-
场景:
- 原切片比较大,代码在原切片基础上创建小切片
- 原底层数组在内存中有引用,得不到释放
-
可使用
copy替代re-slice
总结
- 使用make()函数初始化切片:当你需要创建一个切片并指定其长度和容量时,使用内置的make()函数来分配底层数组的内存。这样可以确保切片有足够的容量来存储元素,避免后续不必要的内存重新分配。
slice := make([]int, 0, 10) // 创建长度为0,容量为10的切片
- 避免过多的切片扩容:当切片的长度超过了其容量时,Go语言会自动扩容切片,重新分配更大的底层数组,并将原始数据复制到新数组中。这种扩容操作会导致额外的内存分配和数据复制,影响性能。因此,如果你知道切片的最大长度,最好在创建时就指定足够的容量,避免过多的扩容操作。
- 使用切片的截取功能:切片提供了截取(slicing)的功能,可以通过指定起始索引和结束索引来获取一个新的切片。这个操作不会分配新的内存,而是共享底层数组。这在某些情况下可以避免创建新的切片,节省内存开销。
- 及时释放不再使用的切片:如果你有一个很大的切片,但在后续的程序逻辑中不再使用它,建议及时将其置为nil,以便垃圾回收器能够回收底层数组的内存。这样可以避免占用过多的内存空间。
Map预分配内存
-
创建 map 时指定大小,时间上也会有优化
-
分析:
- 不断向 map 中添加元素的操作会触发 map 的扩容
- 提前分配好空间可以减少内存拷贝和 Rehash 的消耗
- 建议根据实际需求提前预估好需要的空间
字符串处理
创建的字符串拼接方式:
-
使用 + 拼接字符串的性能最差,
strings.Builder和bytes.Buffer接近,strings.Builder更快 -
分析:
-
字符串在 go 语言中是不可变类型,占用内存大小是固定的
-
使用 + 每次都会重新分配内存
-
strings.Builder和bytes.Buffer底层都是 []byte 数组bytes.Buffer转化为字符串时重新申请了一块空间strings.Builder直接将底层的 []byte 转换成了字符串类型返回
-
内存扩容策略,不需要每次拼接重新分配内存
-
-
进一步性能提升:
- 使用字符串预分配方法:
Grow
- 使用字符串预分配方法:
空结构体
使用空结构体节省内存
-
空结构体
struct{}实例不占据任何的内存空间 -
可作为各种场景下的占位符使用
- 节省资源
- 空结构体本身具备很强的语义,即这里这不需要任何值,仅作为占位符
-
实现
set,可以考虑用 map 来替代- 对于这个场景,只需要用到 map 的键,而不需要值
- 即使是将 map 的值设置为 bool 类型,也会多占据一个字节空间