官方标准Go编译器中应该知道的一些优化

官方标准Go编译器

  1. 简称gc(Go compiler,非Garbage Collection)

  2. 另一款官方Go编译器为gccgo,主要做为一个参考编译器,帮助发现gc中的bugs和改善官方文档。目前从编译速度和编译出的代码质量(正确性和执行速度)都大大落后于gc。

  3. Go设计中推荐使用编译器优化来弥补一些语言中缺失的小功能。

字符串和字节切片之间的转换

一般情况下,这样的转化需要复制一份底层的字节序列。但是gc编译器做了一些优化,使得下面这些情况下的这样的转化无需复制底层的字节序列:

  1. 紧跟range关键字的从字符串到字节切片的转换;

  2. 映射元素读取索引语法中被用做键值的从字节切片到字符串的转换;

  3. 字符串比较表达式中被用做比较值的从字节切片到字符串的转换;

  4. (至少有一个被衔接的字符串值为非空字符串常量的)字符串衔接表达式中的从字节切片到字符串的转换

优化1:紧跟range关键字的从字符串到字节切片的转换

//BenchmarkRangeF
//BenchmarkRangeF-12         2228336           625 ns/op           0 B/op           0 allocs/op
//BenchmarkRangeG
//BenchmarkRangeG-12         1248417           973 ns/op        2048 B/op           1 allocs/op
var gogogo = strings.Repeat("Go", 1024)

func f1() {
    for range []byte(gogogo) {
    }
}
func g1() {
    bs := []byte(gogogo)
    for range bs {
    }
}

func BenchmarkRangeF(b *testing.B) {
    b.ReportAllocs()
    for i:=0; i<b.N; i++ {
        f1()
    }

}
func BenchmarkRangeG(b *testing.B) {
    b.ReportAllocs()
    for i:=0; i<b.N; i++ {
        g1()
    }
}

优化2:映射元素读取索引语法中被用做键值的从字节切片到字符串的转换

优化3:一个字符串比较表达式中被用做比较值的字节切片到字符串的转换

优化4:含非空字符串常量的字符串衔接表达式中从字节切变到字符串的转换

优化5:[]rune(aString)转换的时间和空间复杂度都是O(n),但是len([]rune(aString))中的此转换不需开辟内存

优化6:字符串衔接表达式只需开辟一次内存,无论需要衔接多少个字符串

优化7:for i := range anArrayOrSlice {anArrayOrSlice[i] = zeroElement}形式将被优化为一个内部的memclr操作

优化8:for k = range m {delete(m, k)}形式将被优化为一个内部的map清空操作

优化9:尺寸不大于4个原生字(即int)并且字段数不超过4个的结构体值被视为是小尺寸值

优化10:接口值包裹指针值比包裹其它类型的值要快,因为少开辟一次内存

优化11:接口值包裹0-255的整数也很快(但是比直接包裹 指针还是慢一点)

BCE(Bounds Check Elimination)优化

Go是一门内存安全的语言。检查下标越界是保证内存安全的重要举措之一。但另一方面检查下标越界也耗费一些CPU计算。事实上绝大部分的下标越界检查都不会发现有问题的。这就是维护内存安全的代价。

在某些情形下,编译器在代码编译阶段可以确定某些下标越界检查是不必要的从而可以避免这些检查,这样将提升程序运行效率。

编译器并不总是足够得聪明,有时需要人为干预引导编译器来消除一些下标越界检查。

参考

官方标准Go编译器中应该知道的一些优化

最后更新于

这有帮助吗?