go cg 垃圾回收 友好的代码
2022年1月20日打开GC日志
linux mac 只要在程序运行前加上环境变量 GODEBUG=gctrace=1
1 2 3 4 5 6 |
GODEBUG=gctrace=1 go test -bench=. #.代表所有方法 GODEBUG=gctrace=1 go run main.go GODEBUG=gctrace=1 go test -bench=BenchmarkPassingArrayWithRef #代表BenchmarkPassingArrayWithRef这一个方法 GODEBUG=gctrace=1 go test -bench=BenchmarkPassingArrayWithRef > gc.log #直接写道gc.log文件里面 go test -bench=BenchmarkPassingArrayWithValue -trace=trace_ref # 直接生成trace_ref 文件让你查看 go tool trace trace_ref #用这个命令查看垃圾回收的 二进制文件 |
windows 上
1 2 |
set GOGCTRACE=1 set GODEBUG=gctrace=1 |
将cg写道日志中
1 |
xxx.exe 2> gc.log |
测试代码 pass_array_test.go 可以看出直接赋予地址值 比 直接赋予变量效率高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package gc_friendly import ( "testing" ) const NumOfElems = 1000 /** * 定义一个结构体 */ type Content struct { Detail [10000]int } /** * 没有地址指向写入 */ func withValue(arr [NumOfElems]Content) int { return 0 } /** * 有地址指向写入 */ func withReference(arr *[NumOfElems]Content) int { return 0 } /** * 直接传入变量运行 */ func BenchmarkPassingArrayWithValue(b *testing.B) { var arr [NumOfElems]Content b.ResetTimer() for i := 0; i < b.N; i++ { withValue(arr) } b.StopTimer() } /** * 测试直接传入地址的运行时间 */ func BenchmarkPassingArrayWithRef(b *testing.B) { var arr [NumOfElems]Content b.ResetTimer() for i := 0; i < b.N; i++ { withReference(&arr) } b.StopTimer() } |
测试代码 知道变量所需长度就直接定义好长度效率高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
package gc_friendly import "testing" const times = 1000 const numOfElems = 100000 /** * 测试不同的声明方式那种往后追加变量的方式快 * s := []int{} */ func TestAutoGrow(t *testing.T) { for i := 0; i < times; i++ { s := []int{} for j := 0; j < numOfElems; j++ { s = append(s, j) } } } func BenchmarkAutoGrow(b *testing.B) { for i := 0; i < b.N; i++ { s := []int{} for j := 0; j < numOfElems; j++ { s = append(s, j) } } } /** * 测试不同的声明方式那种往后追加变量的方式快 * s := make([]int, 0, 100000) */ func TestProperInit(t *testing.T) { for i := 0; i < times; i++ { s := make([]int, 0, 100000) for j := 0; j < numOfElems; j++ { s = append(s, j) } } } func BenchmarkProperInit(b *testing.B) { for i := 0; i < b.N; i++ { s := make([]int, 0, numOfElems) for j := 0; j < numOfElems; j++ { s = append(s, j) } } } /** * 测试不同的声明方式那种往后追加变量的方式快 * s := make([]int, 0, 800000) */ func TestOverSizeInit(t *testing.T) { for i := 0; i < times; i++ { s := make([]int, 0, 800000) for j := 0; j < numOfElems; j++ { s = append(s, j) } } } func BenchmarkOverSizeInit(b *testing.B) { for i := 0; i < b.N; i++ { s := make([]int, 0, numOfElems*8) for j := 0; j < numOfElems; j++ { s = append(s, j) } } } |
使用 strings.Builder 连接字符串效率最高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
package concat_string import ( "bytes" "fmt" "strconv" "strings" "testing" ) const numbers = 100 /** * 直接设置为字符串 使用 Sprintf 连接 */ func BenchmarkSprintf(b *testing.B) { b.ResetTimer() for idx := 0; idx < b.N; idx++ { var s string for i := 0; i < numbers; i++ { s = fmt.Sprintf("%v%v", s, i) } } b.StopTimer() } /** * 使用+=连接 */ func BenchmarkStringAdd(b *testing.B) { b.ResetTimer() for idx := 0; idx < b.N; idx++ { var s string for i := 0; i < numbers; i++ { s += strconv.Itoa(i) } } b.StopTimer() } /** * 使用 strings.Builder */ func BenchmarkStringBuilder(b *testing.B) { b.ResetTimer() for idx := 0; idx < b.N; idx++ { var builder strings.Builder for i := 0; i < numbers; i++ { builder.WriteString(strconv.Itoa(i)) } _ = builder.String() } b.StopTimer() } /** * 使用bytes.Buffer */ func BenchmarkBytesBuf(b *testing.B) { b.ResetTimer() for idx := 0; idx < b.N; idx++ { var buf bytes.Buffer for i := 0; i < numbers; i++ { buf.WriteString(strconv.Itoa(i)) } _ = buf.String() } b.StopTimer() } |