[ Go高质量编程简介及性能调优 | 青训营笔记 ]

145 阅读4分钟

[ Go高质量编程简介及性能调优 | 青训营笔记 ]

这是我参与「第五届青训营」伴学笔记创作活动的第 3 天

前言

本节主要简要介绍了高质量编程的定义和原则

一.本课重点

  • 高质量编程

    • 高质量编程简介
    • 编码规范
    • 性能优化建议
  • 性能调优实战

    • 性能调优简介
    • 性能分析工具
    • pprof实战
    • 性能调优案例

二.详细内容

1.编码规范

代码格式

可以使用 gofmt 和 goimports 工具自动格式化代码。对于 GoLand,可以在 Settings - Tools - Actions on Save 中打开 Reformat codeOptimize imports(不出意外的话,它们应当是默认启用的)。也可通过 Ctrl+Alt+L 快捷键格式化代码(其实所有 Jetbrains 的 IDE 都支持使用这个快捷键格式化代码)。

注释

一个好的代码注释应当做到以下几点:

  • 解释代码作用;

  • 解释代码如何做的;

  • 解释代码实现的原因;

  • 解释代码什么情况会出错;

  • 公共符号始终要注释(但是不需要注释实现接口的方法)。

2. 命名规范

变量

  • 简洁胜于冗长;
  • 缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写(例如使用 ServerHTTP 而不是 ServerHttp,使用 XMLHTTPRequest 或是 xmlHTTPRequest 而不是 XmlHttpRequest);
  • 变量距离其被使用的地方越远,则需要携带更多的上下文信息(尤其是全局变量)。

3.性能优化建议

简介:

  • 性能优化的前提是满足正确可靠、简洁清晰等质量因素
  • 性能优化是综合评估,有时候时间效率和空间效率可能对立,所谓时间换空间空间换时间

Benchmark

性能表现需要实际数量来衡量,Go语言提供了支持基准性能测试的benchmark工具

// 一个例子
// from fib.go
func Fib(n int) int {
    if n < 2 {
        return n
    }
    return Fib(n - 1) + Fib(n - 2)
}

// from fib_test.go
func BenchmarkFib10(b *testing.B) {
    // run the Fib funciton b.N times
    for n := 0; n < b.N; n++ {
        Fib(10
    }
}
复制代码

通过go test -bench=. -benchmen来进行测试

运行结果说明:

image.png

Slice预分配内存

尽可能在使用make()初始化切片的时候就提供容量信息,执行时间会差很多

究其原因是因为

  • 切片本质是一个数组片段的描述包括数字指针、片段的长度以及片段的容量
  • 切片操作并不复制切片指向的元素
  • 创建一个新的切片会复用原来切片的底层数组

image.png

另一个陷阱:大内存未释放

有一种情况,原切片由大量元素构成,但是我们在原切片的基础上切片,虽然只使用了很小一段,但底层数组在内存中仍然占据了大量的空间,得不到释放。

这个时候我们可以用copy代替re-slice

Map预分配内存

与Slice相似地,如果初始化size也可以很大程度上优化性能,分析如下:

  • 不断向map中添加元素会触发map的扩容
  • 提前分配好空间可以减少内存拷贝和Rehash的消耗

strings.Builder

在字符串拼接的过程中,使用strings.Builder往往比直接+要快,分析如下:

  • 字符串在Go语言中是不可变类型,占用内存大小是固定的
  • 使用+每次都会重新分配内存
  • strings.Builder, bytes.Buffer底层都是[]byte数组
  • 内存扩容策略,不需要每次拼接重新分配内存
// 一个strings.Builder例子
func StrBuilder(n int, str string) string {
    var builder strings.Builder
    for i := 0; i < n; i++ {
        builder.WriteString(str)
    }
    return builder.String()
}
复制代码

空结构体

使用空结构体节省内存,分析如下:

  • 空结构体struct{}实例不占据任何的内存空间

  • 可作为各种场景下的占位符使用

    • 节省资源
    • 空结构体本身具备很强的语义,不需要任何值,仅作为占位符
// 一个例子
func EmptyStructMap(n int) {
    m := make(map[int]struct{})
    for i := 0; i < n; i++ {
        m[i] = struct{}{}
    }
}

func BoolMap(n int){
    m := make(map[int]bool)
    for i := 0; i < n; i++ {
        m[i] = false
    }
}
// 比较性能即可

三.实战例子

可以根据字节课程实验,对于小白难了

四.课程总结

了解了Go高质量编程简介及性能调优,守护很多。

五.参考文献

  • 字节内部课:高质量编程与性能调优实战