本文总结了 8 种 Golang 性能优化技术,旨在帮助开发人员避免常见的性能陷阱。
你可能习惯于使用 goroutines 并发运行函数,觉得这样很酷,对吗? 但实际上,过多的 goroutines 会降低执行速度。 每个 goroutine 都会消耗一定量的内存,因此如果程序运行数百万个 goroutine,就会消耗大量内存。
避免以下做法:
for _, Item := range bigList { go process(item)}
尝试使用信号量进行限制:
sem := make(chan struct{}, 100) // 限制为 100 个 goroutinesfor _, item := range bigList { sem <- struct{}{} go func(i Item) { defer func() { <-sem }() process(i) }(item)}
通道非常适合进程间通信,但也可能很棘手。 未缓冲的通道可能会意外阻塞程序。
避免以下做法:
ch := make(chan int)
必要时使用缓冲通道:
ch := make(chan int, 100) // 缓冲区大小为 100
缓冲通道可确保发送方在接收方尚未准备好接收任何数据时不会被卡住。 这有助于简化流程,尤其是在处理大规模事务时。
全局变量起初看似简单,但会带来很多问题。 全局变量会导致难以跟踪更改,并可能导致错误。
避免以下做法:
var counter intfunc increment() { counter++}
改为传递变量:
func increment(counter int) int { return counter + 1}counter := 0counter = increment(counter)
我见过全局变量泛滥的代码,调试起来简直是噩梦。 保持本地化会让代码更简洁、更快速。
切片是 Golang 中最常用的数据结构之一,因此明智地使用它们以避免无意的数据复制至关重要。
避免不必要的复制:
// 避免:创建新切片newSlice := make([]int, len(oldSlice))copy(newSlice, oldSlice)// 首选:使用原始切片newSlice := oldSlice[:]
此外,在切片中添加数据时,如果知道数据量可能会有多大,请考虑预先分配容量。
预先分配容量:
s := make([]int, 0, 100) // 容量为 100
这有助于避免多次内存分配并加快处理速度。
有必要建立一种机制来检查代码和模块的效率。 Go 的内置工具可以帮助你查看程序在哪些方面花费了大量时间。
使用分析工具:
import ( "runtime/pprof" "os")func main() { f, _ := os.Create("cpu.prof") pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() // 业务代码}
运行分析工具后,你可能会发现,你认为速度很快的某个函数实际上拖慢了速度。 修复它可以加快程序的执行速度。
Go 有一些非常棒的库,而我犯过的一个错误就是在我的项目中引入了很多 Go 已经提供的功能。 强烈建议在编写代码之前,先检查 Go 是否已经提供了你所需要的功能。
避免重新发明轮子进行排序:
func mySort(data []int) { // 自定义排序}
使用内置的排序包:
import "sort"sort.Ints(data)
内存分配需要时间。 如果在短时间内创建了很多临时对象,创建这些对象所花费的时间可能会影响性能。
尽可能重用对象:
var buffer bytes.Bufferfor i := 0; i < 1000; i++ { buffer.Reset() buffer.WriteString("Some data") process(buffer.Bytes())}
通过重用缓冲区,您可以避免每次都分配新内存,从而显着加快循环速度。
Go 的每个新版本都会带来性能改进,因此请始终使用最新版本以从这些增强功能中受益。
许多人倾向于坚持使用旧版本,因为更新看起来很麻烦,但升级通常可以使您的程序运行得更快,而无需更改任何代码!