go官方标准编译器中实现的优化锦集汇总-Go夜读

本文介绍了Go语言的多项优化内容,包括字符串与字节切片转换、循环操作、结构体值处理、接口值包裹等方面的优化。还阐述了BCE(Bounds Check Elimination)优化,即编译器在某些情形下可避免不必要的下标越界检查,提升程序运行效率,有时需人为引导。

感谢 Go夜读
感谢 Go101

优化1

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

package main

import (
	"strings"
	"testing"
)

var gogogo = strings.Repeat("Go", 1024)

func f() {
	for range []byte(gogogo) {
	}
}

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

func TestT1(t *testing.T) {
	t.Log(testing.AllocsPerRun(1, f)) // 0
	t.Log(testing.AllocsPerRun(1, g)) // 1
}

优化2

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

package main

var name = bytes.Repeat([]byte{'x'}, 33)

var m, s = make(map[string]string, 10), ""

func f() {
	s = m[string(name)]
}

func g() {
	key := string(name)
	s = m[key]
}

func h() {
	m[string(name)] = "Golang"
}

func TestT2(t *testing.T) {
		t.Log(testing.AllocsPerRun(1, f)) // 0
		t.Log(testing.AllocsPerRun(1, g)) // 1
		t.Log(testing.AllocsPerRun(1, h)) // 1
}

优化3

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

package main

import "testing"

// optimize 3

var x = []byte{1023: 'x'}
var y = []byte{1023: 'y'}
var b bool

func f() {
	b = string(x) != string(y)
}

func g() {
	sx, sy := string(x), string(y)
	b = sx == sy
}

func TestT3(t *testing.T) {
		t.Log(testing.AllocsPerRun(1, f)) // 0
		t.Log(testing.AllocsPerRun(1, g)) // 2
}

优化4

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

package main

import "testing"

// optimize 4

var x = []byte{1023: 'x'}
var y = []byte{1023: 'y'}
var s string

func f() {
	s = ("-" + string(x) + string(y))[1:]
}

func g() {
	s = string(x) + string(y)
}

func TestT4(t *testing.T) {
	t.Log(testing.AllocsPerRun(1, f)) // 1
	t.Log(testing.AllocsPerRun(1, g)) // 3
}

优化5

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

package main

import (
	"strings"
	"testing"
)

// optimize 5

var GoGoGo = strings.Repeat("Go", 100)

func f() {
	_ = len([]rune(GoGoGo))
}

func g() {
	_ = len([]byte(GoGoGo))
}

func TestT5(t *testing.T) {
	t.Log(testing.AllocsPerRun(1, f)) // 0
	t.Log(testing.AllocsPerRun(1, g)) // 1
}

优化6

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

package main

import "testing"

// optimize 6

var x, y, z, w = "Hello ", "world! ", "Let's ", "Go!"
var s string

func f() {
	s = x + y + z + w
}

func g() {
	s = x + y
	s += z
	s += w
}

func TestT6(t *testing.T) {
	t.Log(testing.AllocsPerRun(1, f)) // 1
	t.Log(testing.AllocsPerRun(1, g)) // 3
}

优化7

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

package main

import "testing"

// optimize 7

const N = 1024 * 100

var a [N]int

func clearArray() {
	for i := range a {
		a[i] = 0
	}
}

func clearSlice() {
	s := a[:]
	for i := range s {
		s[i] = 0
	}
}

func clearArrayPtr() {
	for i := range &a {
		a[i] = 0
	}
}

func BenchmarkT71(b *testing.B) {
	for i := 0; i < b.N; i++ {
		clearArray()
	}
}

func BenchmarkT72(b *testing.B) {
	for i := 0; i < b.N; i++ {
		clearSlice()
	}
}

func BenchmarkT73(b *testing.B) {
	for i := 0; i < b.N; i++ {
		clearArrayPtr()
	}
}

// Test Result

// goos: windows
// goarch: amd64
// pkg: github.com/gin-gonic/examples/grpc
// BenchmarkT71
// BenchmarkT71-4   	   53283	     22134 ns/op
// BenchmarkT72
// BenchmarkT72-4   	   53474	     22192 ns/op
// BenchmarkT73
// BenchmarkT73-4   	   27148	     41292 ns/op
// PASS
// coverage: [no statements]
//
// Process finished with exit code 0

优化8

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


优化9

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

package main

import "testing"

// optimize 9

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

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: windows
// goarch: amd64
// pkg: github.com/gin-gonic/examples/grpc
// Benchmark_Range1
// Benchmark_Range1-4   	 3486524	       332 ns/op
// Benchmark_Range2
// Benchmark_Range2-4   	 2941167	       409 ns/op
// Benchmark_Range3
// Benchmark_Range3-4   	 1824019	       658 ns/op
// Benchmark_Range4
// Benchmark_Range4-4   	 2939377	       411 ns/op
// Benchmark_Range5
// Benchmark_Range5-4   	  352924	      3447 ns/op
// Benchmark_Range6
// Benchmark_Range6-4   	  307684	      3710 ns/op
// PASS
// coverage: [no statements]
//
// Process finished with exit code 0

优化10

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

package main

import "testing"

// optimize 10

var p, p2 = new([100]int), new([100]int)
var ip interface{}

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)
	}
}

func Benchmark_PointerAssign(b *testing.B) {
	for i := 0; i < b.N; i++ {
		p = p2
	}
}

// goos: windows
// goarch: amd64
// pkg: github.com/gin-gonic/examples/grpc
// Benchmark_BoxPointer
// Benchmark_BoxPointer-4      	1000000000	         0.807 ns/op
// Benchmark_PointerAssert
// Benchmark_PointerAssert-4   	1000000000	         0.891 ns/op
// Benchmark_PointerAssign
// Benchmark_PointerAssign-4   	1000000000	         0.662 ns/op
// PASS
// coverage: [no statements]
//
// Process finished with exit code 0

优化11

接口值包裹0-255的整数也很快 1.15开始

从Go工具链1.15开始, 在Go运行时内部维护着一个0-255的小数组. 但包裹0-255的整数时, 将直接包裹此数组的相应元素的指针而少开辟一次内存.
但是比直接包裹指针还是慢一点

package main

import "testing"

var x, y = 255, 256
var iy, ix interface{}

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: windows
// goarch: amd64
// pkg: github.com/gin-gonic/examples/grpc
// Benchmark_x
// 1.14
// Benchmark_x-4   	59999100	        17.4 ns/op
// 1.15
// Benchmark_x-4   	59999100	        2.98 ns/op
// Benchmark_y
// Benchmark_y-4   	70463887	        17.2 ns/op
// PASS
// coverage: [no statements]

BCE (Bounds Check Elimination) 优化

Go是一门内存安全的语言. 检查下标越界是保证内存安全的重要举措之一. 但另一方面检查下标越界也耗费一些CPU计算. 事实上绝大部分的下标越界检查都不会发现有问题的. 这就是维护内存安全的代价.
在某些情形下, 编译器在代码编译阶段可以确定某些下标越界检查是不必要的从而可以避免这些检查, 这样将提升程序运行效率
编译器并不总是足够的聪明, 有时需要人为干预引导编译器来消除一些下标越界检查.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值