Go 官方标准编译器中所做的优化

简介: Go 官方标准编译器中所做的优化

本文是对#102 Go 官方标准编译器中实现的优化集锦汇总 内容的记录与总结.

微信截图_20230925185456.png

优化1-4: 字符串和字节切片之间的转化

微信截图_20230925185509.png

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

package main
import (
  "fmt"
  "strings"
  "testing"
)
var cs10086 = strings.Repeat("shuang!", 10086)
func main() {
  fmt.Println(testing.AllocsPerRun(1, f)) //0
  fmt.Println(testing.AllocsPerRun(1, g)) //1
}
func f() {
  for range []byte(cs10086) {
  }
}
func g() {
  bs := []byte(cs10086)
  for range bs {
  }
}

f没有开辟内存,g开辟了一次内存.

微信截图_20230925185606.png

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

package main
import (
  "bytes"
  "fmt"
  "testing"
)
var name = bytes.Repeat([]byte{'x'}, 188)
var m = make(map[string]string, 10)
var s = ""
func main() {
  fmt.Println(testing.AllocsPerRun(1, f2)) //0
  fmt.Println(testing.AllocsPerRun(1, g2)) //1
  fmt.Println(testing.AllocsPerRun(1, h2)) //1
}
func f2() {
  s = m[string(name)] // 有效
}
func g2() {
  key := string(name)
  s = m[key] // 无效
}
func h2() {
  m[string(name)] = "Golang" // 无效
}

微信截图_20230925185645.png

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

package main
import (
  "fmt"
  "testing"
)
var x = []byte{1023: 'x'}
var y = []byte{1023: 'y'}
var b bool
func main() {
  fmt.Println(testing.AllocsPerRun(1, f3)) //0
  fmt.Println(testing.AllocsPerRun(1, g3)) //2
}
func f3() {
  b = string(x) != string(y)
}
func g3() {
  sx, sy := string(x), string(y)
  b = sx == sy
}

微信截图_20230925185736.png

4.含 非空字符串常量 的字符串衔接表达式中的 从字节切片到字符串的转换

package main
import (
  "fmt"
  "testing"
)
var p = []byte{1023: 'p'}
var q = []byte{1023: 'q'}
var str string
func main() {
  fmt.Println(testing.AllocsPerRun(1, f4)) //1
  fmt.Println(testing.AllocsPerRun(1, g4)) //3
}
func f4() {
  str = ("-" + string(p) + string(q))[1:]
}
func g4() {
  str = string(p) + string(q)
}

微信截图_20230925185823.png

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


Go 1.12引入

package main
import (
  "fmt"
  "strings"
  "testing"
)
var shuang = strings.Repeat("shuang!", 10086)
func main() {
  fmt.Println(testing.AllocsPerRun(1, f5)) //0
  fmt.Println(testing.AllocsPerRun(1, g5)) //1
}
func f5() {
  _ = len([]rune(shuang))
}
func g5() {
  _ = len([]byte(shuang)) //未对len([]byte(aString))做优化
}

微信截图_20230925185858.png

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

package main
import (
  "fmt"
  "testing"
)
var h, i, j, k = "Hello", "World", "Let's", "Go"
var str6 string
func main() {
  fmt.Println(testing.AllocsPerRun(1, f6)) //1
  fmt.Println(testing.AllocsPerRun(1, g6)) //3
}
func f6() {
  str6 = h + i + j + k
}
func g6() {
  str6 = h + i
  str6 += j
  str6 += k
}

微信截图_20230925185938.png

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

package main
const N = 1024 * 100
var arr [N]int
func clearArray() {
  for i := range arr {
    arr[i] = 0
  }
}
func clearSlice() {
  sli := arr[:]
  for i := range sli {
    sli[i] = 0
  }
}
func clearArrayPtr() {
  for i := range &arr {
    arr[i] = 0
  }
}

微信截图_20230925190013.png

benchmark:

package main
import (
  "testing"
)
func BenchmarkTest1(b *testing.B) {
  for i := 0; i < b.N; i++ {
    clearArray()
  }
}
func BenchmarkTest2(b *testing.B) {
  for i := 0; i < b.N; i++ {
    clearSlice()
  }
}
func BenchmarkTest3(b *testing.B) { //无效
  for i := 0; i < b.N; i++ {
    clearArrayPtr()
  }
}

执行结果:

goos: darwin
goarch: amd64
pkg: xxxx
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
BenchmarkTest1-8           73000             15309 ns/op
BenchmarkTest2-8           76464             15167 ns/op
BenchmarkTest3-8           40194             30096 ns/op
PASS
ok      xxxx    4.213s

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

image.png

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

package main
type S1 struct {
  a int
}
type S2 struct {
  a, b int
}
type S3 struct {
  a, b, c int
}
type S4 struct {
  a, b, c, d int
}
type S5 struct {
  a, b, c, d, e int
}
type S6 struct {
  a, b, c, d, e, f int
}
var ss1, ss2, ss3, ss4, ss5, ss6 = make([]S1, 1000), make([]S2, 1000), make([]S3, 1000), make([]S4, 1000), make([]S5, 1000), make([]S6, 1000)
var x1, x2, x3, x4, x5, x6 int

benchmark:

package main
import "testing"
func Benchmark_Range1(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss1 {
      x1 = v.a
    }
  }
}
func Benchmark_Range2(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss2 {
      x2 = v.a
    }
  }
}
func Benchmark_Range3(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss3 {
      x3 = v.a
    }
  }
}
func Benchmark_Range4(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss4 {
      x4 = v.a
    }
  }
}
func Benchmark_Range5(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss5 {
      x5 = v.a
    }
  }
}
func Benchmark_Range6(b *testing.B) {
  for i := 0; i < b.N; i++ {
    for _, v := range ss6 {
      x6 = v.a
    }
  }
}

执行结果:

goos: darwin
goarch: amd64
pkg: xxxx
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
Benchmark_Range1-8       4759434               248.4 ns/op
Benchmark_Range2-8       3910621               306.0 ns/op
Benchmark_Range3-8       3735921               328.9 ns/op
Benchmark_Range4-8       3677784               325.9 ns/op
Benchmark_Range5-8        814666              1517 ns/op
Benchmark_Range6-8        728656              1568 ns/op
PASS
ok      xxxx     8.868s

微信截图_20230925190232.png

因为很多一等公民,其底层结构体的元素,都没有超过4个




10.接口值包裹 指针值 比 包裹 其他类型的值 要快

package main
var p, p2 = new([100]int), new([100]int)
var ip interface{}
package main
import "testing"
func Benchmark_PointerAssign(b *testing.B) {
  for i := 0; i < b.N; i++ {
    p = p2
  }
}
func Benchmark_BoxPointer(b *testing.B) {
  for i := 0; i < b.N; i++ {
    ip = p
  }
}
func Benchmark_PointerAssert(b *testing.B) {
  for i := 0; i < b.N; i++ {
    p = ip.(*[100]int)
  }
}
goos: darwin
goarch: amd64
pkg: xxxx
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
Benchmark_PointerAssign-8       1000000000               0.5251 ns/op          0 B/op          0 allocs/op
Benchmark_BoxPointer-8          1000000000               0.5833 ns/op          0 B/op          0 allocs/op
Benchmark_PointerAssert-8       1000000000               0.6418 ns/op          0 B/op          0 allocs/op
PASS
ok      xxxx   2.372s

微信截图_20230925190334.png

微信截图_20230925190343.png

11.接口值包裹 指针值 比 包裹 其他类型的值 要快


Go 1.15新增优化

package main
var x,y = 255,256
var ix,iy interface{}
package main
import "testing"
func Benchmark_x(b *testing.B) {
  for i := 0; i < b.N; i++ {
    ix = x
  }
}
func Benchmark_y(b *testing.B) {
  for i := 0; i < b.N; i++ {
    iy = y
  }
}
goos: darwin
goarch: amd64
pkg: xxxx
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
Benchmark_x-8           565624285                2.033 ns/op           0 B/op          0 allocs/op
Benchmark_y-8           92127024                12.71 ns/op            8 B/op          1 allocs/op
PASS
ok      xxxx     2.653s

微信截图_20230925190447.png

12.Bounds Check Elimination

微信截图_20230925190500.png

微信截图_20230925190507.png

微信截图_20230925190514.png

微信截图_20230925190521.png

微信截图_20230925190528.png

微信截图_20230925190537.png

目录
相关文章
|
2月前
|
数据采集 安全 Go
Go并发优化的9大技巧,效果立竿见影
Go并发优化的9大技巧,效果立竿见影
138 0
|
2月前
|
存储 算法 编译器
掌握Go语言:探索Go语言递归函数的高级奥秘,优化性能、实现并发、解决算法难题(28)
掌握Go语言:探索Go语言递归函数的高级奥秘,优化性能、实现并发、解决算法难题(28)
|
2月前
|
Go 索引
Go 1.22 slices 库的更新:高效拼接、零化处理和越界插入优化
本文详细介绍了 Go 1.22 版本中 slices 库的更新内容,总结起来有三个方面:新增了 Concat 函数、对部分函数新增了零化处理的逻辑和对 Insert 函数进行了越界插入优化
126 1
Go 1.22 slices 库的更新:高效拼接、零化处理和越界插入优化
|
2月前
|
Java 测试技术 Go
使用go的内置运行时库调试和优化程序
【5月更文挑战第18天】在本文中,作者探讨了如何为运行时程序添加剖析支持以优化性能。他们面临的问题是一个程序需要找出平方根为整数且逆序平方等于其逆序的数字。他们首先展示了原始代码,然后使用`runtime`库进行优化,将计算和调用功能分离,同时记录CPU和内存使用情况。
40 4
|
2月前
|
监控 安全 Go
【Go语言专栏】Go语言中的并发性能分析与优化
【4月更文挑战第30天】Go语言以其卓越的并发性能和简洁语法著称,通过goroutines和channels实现并发。并发性能分析旨在解决竞态条件、死锁和资源争用等问题,以提升多核环境下的程序效率。使用pprof等工具可检测性能瓶颈,优化策略包括减少锁范围、使用无锁数据结构、控制goroutines数量、应用worker pool和优化channel使用。理解并发模型和合理利用并发原语是编写高效并发代码的关键。
|
2月前
|
Go C++
【力扣】2645. 构造有效字符串的最小插入数(动态规划 贪心 滚动数组优化 C++ Go)
【2月更文挑战第17天】2645. 构造有效字符串的最小插入数(动态规划 贪心 滚动数组优化 C++ Go)
34 8
|
2月前
|
监控 Java 编译器
Go语言内存与并发性能综合优化策略
【2月更文挑战第11天】Go语言以其高效的并发处理能力和简洁的内存管理机制成为了现代软件开发中的热门选择。然而,在实际应用中,如何综合优化Go程序的内存使用和并发性能,仍然是一个值得探讨的话题。本文将深入探讨Go语言内存与并发性能的综合优化策略,包括内存布局优化、并发模式设计、资源池化以及性能监控与分析等方面,旨在帮助开发者全面提升Go程序的整体性能。
|
2月前
|
缓存 资源调度 Go
内存模型与调度器在Go语言并发编程中的应用与优化
【2月更文挑战第16天】Go语言以其独特的并发编程模型而备受瞩目,其中内存模型和调度器是支撑其高效并发执行的关键机制。本文将探讨内存模型与调度器在Go语言并发编程中的应用场景,并介绍一些优化策略,帮助开发者更好地利用这些机制,提升程序的性能和稳定性。
|
2月前
|
Java 编译器 Go
Go语言内存管理优化实践
【2月更文挑战第11天】随着Go语言在各个领域的应用日益广泛,对其性能的要求也越来越高。内存管理作为影响程序性能的关键因素之一,对Go语言的优化显得尤为重要。本文将深入探讨Go语言的内存管理机制,并提供一系列实用的内存优化策略,帮助开发者更加高效地利用内存资源,提升Go程序的整体性能。
|
9月前
|
Go 开发者
Go 语言怎么优化重复的 if err != nil 样板代码?
Go 语言怎么优化重复的 if err != nil 样板代码?
66 0