这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
一、本堂课重点内容:
1. 高质量编程
2. 性能调优
- 性能调优简介
- 性能分析工具 pprof 实战
- 性能调优案例
二、详细知识点介绍:
1. 高质量编程
-
高质量编程简介
- 简单性:不要瞎拽技术,能用简单的就不要用复杂的
- 可读性:代码在机器眼中就是0和1,规范的目的是给人看的
- 生产力:效率是第一位
-
编码规范
-
代码格式
gofmt自动格式代码 -
注释
1.解释代码的作用
2.如何做的
3.实现的原因
4.什么时候会出错 -
命名规范
命名是代码规范中很重要的一部分,统一的命名规则有利于提高的代码的可读性,好的命名仅仅通过命名就可以获取到足够多的信息。
1. 包命名:package
保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。包名应该为小写单词,不要使用下划线或者混合大小写。package demo package main2. 文件命名 尽量采取有意义的文件名,简短,有意义,应该为小写单词,使用下划线分隔各个单词。
my_test.go3. 结构体命名
- 采用驼峰命名法,首字母根据访问控制大写或者小写
- struct 申明和初始化格式采用多行,例如下面:
// 多行申明 type User struct{ Username string Email string } // 多行初始化 u := User{ Username: "Nbetray", Email: "Nbetray@qq.com", }4. 接口命名
- 命名规则基本和上面的结构体类型
- 单个函数的结构名以 “er” 作为后缀,例如 Reader , Writer 。
type Reader interface { Read(p []byte) (n int, err error) }5. 变量命名
- 和结构体类似,变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写,但遇到特有名词时,需要遵循以下规则:
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient
- 其它情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID
- 错误示例:UrlArray,应该写成 urlArray 或者 URLArray
- 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头
var isExist bool var hasConflict bool var canManage bool var allowGitHook bool6. 常量命名
常量均需使用全部大写字母组成,并使用下划线分词const APP_VER = "1.0"如果是枚举类型的常量,需要先创建相应类型:
type Scheme string const ( HTTP Scheme = "http" HTTPS Scheme = "https" )7. 关键字
保留字不能用作常量或变量或任何其他标识符名称。 -
控制流程
1.线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支
2.正常流程代码沿着屏幕向下移动
3.提升代码可维护性和可读性
4.故障问题大多出现在复杂的条件语句和循环语句中 -
错误和异常处理
1.error尽可能提供简明的上下文信息链,方便定位问题
2.panic 用于真正异常的情况
3.recover生效范围,在当前 goroutine 的被 defer 的函数中生效
-
-
性能优化建议
-
简介
- 性能优化的前提是满足正确可靠、简洁清晰等质量因素
- 性能优化是综合评估,有时候时间效率和空间效率可能对立
- 避免常见的性能可以保证大部分程序的性能
- 普通应用代码,不要一味地追求程序的性能
- 越高级的性能优化手段越容易出现问题
- 在满足正确可靠、简洁清晰的质量要求的前提下提高程序性能
-
Benchmark
- 能表现需要实际数据衡量
- Go语言提供了支持基准性能测试的benchmark工具
-
Slice
- slice预分配内存:尽可能在使用make()初始化切片时提供容量信息
- 大内存未释放:
- 在已有切片基础上创建切片,不会创建新的底层数组
- 场景:
原切片较大,代码在原切片基础上新建小切片
原底层数组在内存中有引用,得不到释放 - 可使用copy替代re-slice
-
Map
- 预分配内存:建议根据实际需求提前预估好需要的空间
-
字符串处理
- 使用 + 拼接性能最差,strings.Builder与bytes.Buffer相近,但strings.Buffer更快
-
空结构体
- 占位符,省内存
-
atomic包
- 使用
//使用atomic包 type atomiccounter struct { i int32 } func AtomicAddone (c *atomiccounter) { atomic.Addint32(&c.i,1) } //不使用atomic包 type mutexcounter struct { i int32 m Sync.Mutex } func MutexAddone (c *mutexCounter) { c.m.Lock() c.i++ c.m.Unlock() }对比
- 锁的实现是通过操作系统来实现,属于系统调用
- atomic操作是通过硬件实现,效率比锁高
- sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
- 对于非数值操作,可以使用atomic.value,能承载一个interface{}
-
2. 性能调优
-
性能调优简介
三、实践练习例子:
- 性能分析工具 pprof 实战
- 性能调优案例
四、课后个人总结:
- 需掌握良好的编程规范
- 学会分析代码性能,并且逐步调优