没有理想的人不伤心

Golang - 高质量编程和性能调优实战

2025/03/04
1
0

高质量编程和性能调优实战

  1. 如何编写简洁清晰的代码
  2. 常用 go 语言程序优化手段
  3. go 程序性能分析工具
  4. 工作中性能优化的原则和流程

编程规范

代码格式:

可以使用官方工具 go fmt 自动格式化,或者使用 go imports 工具,该工具就是 gofmt+依赖包自动管理,能够自动增删依赖包引用,以及依赖包的排序分类

注释:

注释应该解释:代码作用、如何工作、为什么这样实现、什么情况会出错

**是什么、为什么、会怎样、怎么做 **==》 代码作用、为什么这样实现、什么情况出错、如何工作

包中声明的每个公共的符号、变量、常量、函数以及结构都需要添加注释

任何既不明显也不简短的公共功能必须予以注释

无论长度或复杂程度如何对库中的任何函数都必须进行注释

命名规范

变量命名:

1730695086720-9fb58ad5-db9d-4722-a525-cb6d0101f99e.png

函数命名:

  • 尽量简洁
  • 若包名含有上下文信息,则函数中不需要再带

如:http.serve(…),包名已经表示 http 了,就不用将函数命名为 serveHTTP()

包命名:

1730696062380-bde12a94-ea08-4c84-840b-0d3add98fa82.png

控制流程

  • 避免嵌套,保持正常流程的清晰
  • 线性原则,处理逻辑尽量走直线,避免深层嵌套


image.png

1730696220179-afb4e518-899b-45b1-aecf-bfd04ac7e77c.png

错误和异常处理规范

简单的错误:

1730696595633-48c7696a-158e-4e32-9e8f-76132465bca8.png

复杂错误:

1730696720380-127c2595-fe57-4818-9586-d4bf183845d0.png

如:1730696733670-02161075-103f-4fc3-80cb-b28ddde3512a.png

对于错误的判定:

1730696815507-7f0b43b0-0d63-4222-aa73-fa54fbf1ab4d.png

如:1730696839173-f7391f2c-f422-4b3a-af4f-5bf2b053ed75.png

1730696930501-8933a9f7-e5f0-465f-bc6f-f5654e759cf0.png

如:
1730696944837-dfeff77e-9058-40cb-b901-d2896e660590.png

panic 使用规范

image.png
1730697065844-e425a4d0-274a-4655-9204-611ea50beaa1.png

recover 使用规范:

  • 只能在被 defer 的函数中使用

recover 在嵌套中无法生效、且只有在当前 goroutine 生效,defer 语句执行顺序是后进先出,要注意一下执行顺序

  • 如果需要更多上下文信息,可以 recover 后在 log 中记录当前的调用栈

1730697500878-d68a3baf-8d1f-462f-a415-b13baa3372a2.png

性能优化建议

1730704458586-af41a4ef-24db-4c34-a8e8-b9e55356a049.png

性能如何评估

go 语言官方提供的基准性能测试工具 Benchmark 可以用来衡量程序的性能

go test -bench=. -benchmem

用于 benchmark 性能测试的函数命名以 Benchmark 开头

如:

1730698003031-4f3902ae-0848-4e4f-86a9-0df67ff72519.png

1730697990820-ad636c56-5e23-4b81-b044-0a4f8cf1e24f.png

性能优化建议

slice:

  1. slice 预分配内存,尽可能在使用 make 初始化 slice 时提供容量信息
    特别是在循环中,能大大减少内存分配次数,增加程序性能
  2. 由于 slice 在已有切片上创建新切片时,仍然使用的原来的底层数组,并不会创建新的底层数组
    因此当原底层数组占用非常大的内存时,而新建小切片相较之下很小时,会导致小切片引用大数组,使得内存得不到释放
    可以使用 copy()来代替在原底层数组上创建小切片

切片的本质是一个数组片段的描述,包括底层数组的指针、长度和容量

如:
1730698564472-f8513306-2af8-4eb0-b790-677c6310408c.png

测试:

1730698607107-3a882ef2-7a26-4f69-931f-86768eb48ef6.png

1730698619871-959ae69d-2587-4b60-8f29-04a77ee4a9fe.png


map:类似的,预先分配好内存


使用 strings.Builder 处理字符串

使用“+”直接拼接性能最差,使用 **strings.Builder**最快

strings.Builder > bytes.Buffer > +

原因:

1730700626343-9dff28cc-fe70-433d-b87d-23b2e1308cda.png

1730703942013-b7ac557b-8df3-4250-b3fc-33a98673c7a7.png

空结构体

1730704101988-52ef1ba4-64ac-4308-ae1c-7ad54d4ce4a7.png

如在实现 set 时,set 只需要使用 map 的键,因此可以将结构体作为 map 的值

1730704201382-a9af21ad-10ce-4c56-ab50-c1fa6de4c11a.png

使用 atomic 包而不是 sync.Mutex 进行线程操作

1730704292144-ad6f2a27-26c4-4c3b-96b9-e3c29e4d91cb.png

性能调优

原则:

  1. 数据说话,而不是我觉得
  2. 优化最大瓶颈,忽略细枝末节
  3. 不过早、过度优化

性能分析工具 pprof

用于 go 语言的性能分析工具,主要功能:

  • 性能分析pprof 可以收集程序运行时的性能数据,包括 CPU 使用情况、内存分配、阻塞情况等。
  • 多种数据收集方式:支持实时数据收集(通过程序运行时开启)和静态数据收集(通过生成性能报告文件)。
  • 可视化:生成的性能报告可以以图形方式可视化,帮助开发者更直观地理解程序性能。可以生成调用图、火焰图等。
  • 多种输出格式:支持多种输出格式,如文本、PDF、SVG 等,方便用户分析和共享。

image.png
1731401578513-51744eb4-41f3-400c-ba9a-6deb2371983f.png

通过 https://github.com/wolfogre/go-pprof-practice 项目可以快速熟悉 pprof 工具

项目使用文档:[https://farmerchillax.github.io/2023/07/04/Go%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/](https://farmerchillax.github.io/2023/07/04/Go%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/)

1731402208941-1057aceb-199c-4e41-bd6f-6cb84e86667e.png

内存管理及优化

自动内存管理(Auto memory management)

自动内存管理:由程序语言的运行时系统(runtime)管理回收动态内存

  • 避免手动内存管理,专注业务逻辑
  • 保证内存使用的正确性和安全性,避免 double free use-after-free 等内存漏洞

自动内存管理负责:

  • 为新对象分配空间
  • 找到并维护存活着的对象
  • 自动回收死亡对象的内存

相关概念

GC:Grabage collction,垃圾回收

1731403001238-750822aa-110d-4933-a195-77fa4fe07e9e.png

1731403037021-e3d927ce-10ec-4587-9180-8dfa0adf6ca6.png

更新:2024-11-12 17:18:11
原文: https://www.yuque.com/c10wn/svr7uf/wrkci8t2m0qk5u1l