这是我参与[第五届青训营]笔记创作的第三天
1、高质量编程
编写的代码能够达到正确可靠、简洁清晰的目标可称为高质量代码
简单性:
- 消除“多余的复杂性”,以简单清晰的逻辑编写代码
- 不理解的代码无法修复改进
可读性
- 代码是写给人看的,而不是机器
- 编写可维护代码的第一步是确保代码可读
生产力
- 团队整体工作效率非常重要
1、编码规范
注释
公共符号始终要注释:对代码的功能要注释
- 有一个例外,不需要注释实现接口的方法
注释应该做的
- 注释应该解释代码作用
适合注释公共符号
- 注释应该解释代码如何做的
适合注释实现过程
- 注释应该解释代码实现的原因
适合解释代码的外部因素
提供额外上下文
- 注释应该解释代码什么情况会出错
适合解释代码的限制条件
控制流程
尽量保持正常代码路径为最小缩进
func OneFunc() error {
err := doSomething()
if err == nil {
err := doAnotherThing()
if err == nil {
return nil
}
return err
}
return err
}//bad
func OneFunc() error {
if err := doSomething(); err != nil {
return err
}
if err := doAnotherThing(); err != nil {
return err
}
return nil
}//Good
错误和异常处理
简单错误
- 简单错误指的是仅出现一次的错误,且在其他地方不需要捕获该错误
- 优先使用errors.New来创建匿名变量来直接表示简单错误
- 如果有格式化的需求,使用fmt.Errorf
func defaultCheckRedirect(req *Request,via []*Request) error {
if len(via) >= 10 {
return errors.New("stopped after 10 redirects") //在出现十次错误之后就停止
}
return nil
}
defer
- defer语句会在函数返回前调用
- 多个defer语句是后进先出
func main(){
if true {
defer fmt.Printf("1")
}else {
defer fmt.Printf("2")
}
defer fmt.Printf("3")
}
运行结果31
命名规范
- 简洁
- 缩略词全大写,但当其位于比那辆开头且不需要导出时,使用全小写
- 变量近距离了其被使用的地方越远,则需要携带越多的上下文信息
函数名规范
- 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现
- 函数名尽量简短
- 当foo的包某个函数返回类型Foo时,可以省略类型信息而不导致歧义
- 当名为foo的包某个函数返回类型T时(T并不是Foo),可以再函数名中加入类型信息
代码格式
推荐使用gofmt自动格式化代码
2、性能优化建议:
1、性能优化建议-Benchmark
- 性能表现需要实际数据衡量
- Go语言提供了支持基准性能测试的benchmark工具
go test -bench=. -benchmen
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)
}
}
2、性能优化建议-Slice
slice 预分配内存
尽可能在使用make()初始化切片时提供容量大小
func NoPreAlloc(size int) {
data := make([]int,0)
for k := 0; k <size;k ++{
data = append(data,k)
}
}//没有分配
func PreAlloc(size int) {
data := make([]int,0,size)
for k := 0; k <size;k ++{
data = append(data,k)
}
}//有分配
3、性能优化建议-Map
map 预分配内存
- 不断向map中添加元素的操作会触发map扩容
- 提前分配好空间可以减少内存拷贝和Rehash的消耗
- 建议根据实际需求提前预估好需要的空间
4、性能优化建议-字符串处理
使用strings.Builder
func Plus(n int, str string) string {
s := ""
for i := 0; i < n ; i ++{
s += str
}
return s
}//性能最差
func StrBuilder(n int, str string) string {
var builder strings.Builder
for i := 0; i < n; i++{
builder.WriteString(str)
}
return builder.string()
}//性能第二强
func ByteBuffer(n int,str string) string {
buf := new(bytes.Buffer)
for i := 0; i < n;i ++{
buf.WriteString(str)
}
return buf.String()
}//性能最强
使用strings.Builder
使用 + 拼接性能最差,strings.Builder,bytes.Buffer相近,Strings.Buffer更快
5、性能优化建议-空结构体
空结构体struct{}实例不占据任何的内存空间
func EmptyStructMap(n int) {
m := make(map[int]struct{})
for i := 0;i < n; i ++{
m[i] = false
}
}
实现Set,可以考虑用map来代替
对于这个常见,只需要用到map来代替
即将是将map的值设置为bool类型,也会多占据1个字节空间
2、性能调优实战
原则:
- 要依靠数据不是猜测
- 要定位最大瓶颈而不是细枝末节
- 不要过早优化
- 不要过度优化
性能分析工具pprof
1、性能分析工具pprof-功能简介
- 分析-Profile
- 工具-Tool
- 展示-View
- 采样-Sample
2、实战
func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)
log.SetOutput(os.Stdout)
runtime.GOMAXPROCS(1) //限制CPU使用数
runtime.SetMutexProfileFraction(1) //开启锁调用跟踪
runtime.SetBlockProfileRate(1) //开启阻塞调用跟踪
go func() {
if err := http.ListenAndServe(":6060", nil); err != nil {
log.Fatal(err)
}
os.Exit(0)
}()