一、如何优化Go语言的性能
Go语言是一种现代化编程语言,被广泛应用于网络开发和系统编程领域。其强大的并发性能和简洁的语法使得它成为许多开发者的首选语言。但是,在使用Go语言开发的程序也可能遇到性能瓶颈。接下来我们将利用已有程序探讨一些优化方法。
二、优化工具
(1)pprof;(2)memory profiler;(3)cpu profiler;(4)fgprof;(5)trace;(6)perf。这六种优化工具虽然每种有不同的缺点,但是可以完成大多数的优化。
三、例子
在编写 library 时,我们会关注关键的函数性能,这时可以脱离系统去探讨性能优化,Go 语言的 test 子命令集成了相关的功能,只要我们按照约定来写 Benchmark 前缀的测试函数,就可以实现函数级的基准测试。我们以常见的二维数组遍历为例:
package main
import "testing"
var x = make([][]int, 100)
func init() {
for i := 0; i < 100; i++ {
x[i] = make([]int, 100)
}
}
func traverseVertical() {
for i := 0; i < 100; i++ {
for j := 0; j < 100; j++ {
x[j][i] = 1
}
}
}
func traverseHorizontal() {
for i := 0; i < 100; i++ {
for j := 0; j < 100; j++ {
x[i][j] = 1
}
}
}
func BenchmarkHorizontal(b *testing.B) {
for i := 0; i < b.N; i++ {
traverseHorizontal()
}
}
func BenchmarkVertical(b *testing.B) {
for i := 0; i < b.N; i++ {
traverseVertical()
}
}
执行go test -bench=.
BenchmarkHorizontal-12 102368 10916 ns/op
BenchmarkVertical-12 66612 18197 ns/op
可见横向遍历数组要快得多,这提醒我们在写代码时要考虑 CPU 的 cache 设计及局部性原理,以使程序能够在相同的逻辑下获得更好的性能。除了 CPU 优化,我们还经常会碰到要优化内存分配的场景。只要带上 -benchmem 的 flag 就可以实现了。举个例子,形如下面这样的代码:
logStr := "userid :" + userID + "; orderid:" + orderID
如果优化可读性可以改成:
logStr := fmt.Sprintf("userid: %v; orderid: %v", userID, orderID)
四、个人看法
通过学习,自己学到了goroutine 频繁创建与销毁会给调度造成较大的负担,如果我们发现 CPU 火焰图中schedule,findrunnable 占用了大量 CPU,那么可以考虑使用开源的 workerpool 来进行改进,比较典型的 fasthttp worker pool。如果客户端与服务端之间使用的是短连接,那么我们可以使用长连接。
而且在实际编程或者实际工程中,后端系统往往不只有一个接口,但是压测工具、平台往往只支持单接口压测。所以如果一个系统有10个接口,那么这个服务的真实负载就很难靠人肉模拟了,尽管10个接口已经算是很小的服务了。