go项目性能调优 | 青训营

78 阅读3分钟

前言

一个满足需求能顺利跑起来的项目是合格的,但仅仅只是跑起来是不够的。一个优秀的项目除了满足基本的需求,还要有良好的性能。倘若响应一个事件都要等待许久,会让客户放弃你的软件,转而去投向性能更出色的软件。

在一些性能敏感的事务上,因为一时间会有海量数据处理,差1ms也会造成庞大的性能浪费。

除了时间上的效率,空间效率也是一部分。优秀的空间性能亦能节省许多的存储费用。但时间性能和空间性能往往是此消彼长的。但目前存储设备价格降低,时间效率的提高比空间效率的提高性价比更高.

Go语言基准性能测试

性能的提高并不是靠猜测,也不是使用了什么技巧就会得到性能的提升。而是要依靠工具测量。只有实际数据才能准确表示性能高低。

基准性能测试:benchmark工具

使用方法:

  1. 在后缀"_test.go"的测试文件里写测试函数
  2. 基准测试函数名需要以 BenchmarkXxx(b *testing.B) 命名
  3. go test 命令
go test -bench=. -benchmem

下面是一个简单的例子:

fib_test.go:

package goTest

import "testing"

func Fib(n int)int{
	if n<2{
		return n
	}
	return Fib(n-1)+Fib(n-2)
}

func BenchmarkFib10(b *testing.B){
	for n:=0;n<b.N;n++{
		Fib(10)
	}
}
goos: linux
goarch: amd64
pkg: goTest
cpu: ************
=== RUN   BenchmarkFib10
BenchmarkFib10
BenchmarkFib10-16        5247408               230.0 ns/op             0 B/op          0 allocs/op
PASS
ok      goTest  1.444s

倒数第四行分别是:

  1. 测试的函数名和GOMAXPROCS的值(16:线程数)
  2. 表示执行的次数————b.N的值
  3. 每次执行花费
  4. 每次执行申请内存
  5. 每次执行申请几次内存

根据基准性能测试可以更好的定位程序的性能问题,发现痛点,解决痛点。不得不说go test的工具链十分充足。

性能优化建议

Slice 切片

切片预分配

在使用make初始化切片时,提供容量信息。

因为切片的本质就是一串记录了信息的数组,而数组的拓展需要复制之前的数据和开辟新的内存空间。内存拷贝开销巨大极度浪费性能,所以尽可能提前分配好。让内存尽可能提前分配,减少运行中分配,减少性能损耗。

使用copy替代re-slice

在原来庞大的切片上创建一个小切片时,相当于在原本的切片上引用,未创建新的切片空间。gc不会将原切片清除掉。

而使用copy则会复制那一段切片,这样在原切片失去引用时也会被gc掉。

Map 预分配内存

向map添加元素会造成拓容,理由与切片一样,会造成性能浪费。提前预估分配好map空间,有利于减少内存拷贝开销。

字符串处理

使用strings.Builder / strings.Buffer

尽可能不使用 string + string 这样会造成内存分配开销,如果多次相加的话。 使用BUilder或BUffer会预分配内存,无需每次字符拼接都重新分配内存。