slice 預先分配內存
slice 
 func appendOne(num int) []int {
	var res []int
	for i := 0; i < num; i++ {
		res = append(res, i)
	}
	return res
}
func appendMany(num int) []int {
	res := make([]int, 0, num)
	for i := 0; i < num; i++ {
		res = append(res, i)
	}
	return res
}
複製代碼 
 appendOneappendMany 
 func BenchmarkAppendOne(b *testing.B) {
	num := 10000
	for i := 0; i < b.N; i++ {
		_ = appendOne(num)
	}
}
func BenchmarkAppendMany(b *testing.B) {
	num := 10000
	for i := 0; i < b.N; i++ {
		_ = appendMany(num)
	}
}
複製代碼 
 運行測試app
$ go test -bench=. -benchmem goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkAppendOne-4 23163 50675 ns/op 386296 B/op 20 allocs/op BenchmarkAppendMany-4 96781 12241 ns/op 81920 B/op 1 allocs/op PASS 複製代碼
AppendMany81920 B12241 nsAppendOneslice 
 map 
 func makeMap(num int){
    m := make(map[int]int,num)
    for i:=0;i<len(num);i++{
        m[i]=i
    }
}
複製代碼 
 這能夠減小內存拷貝的開銷,也能夠減小rehash開銷。oop
map中保存值,而不是指針,使用分段map
看下面的列子,map分別保存指針,值。性能
func timeGC() time.Duration {
	start := time.Now()
	runtime.GC()
	return time.Since(start)
}
func mapPointer(num int) {
	m := make(map[int]*int, num)
	for i := 0; i < num; i++ {
		m[i] = &i
	}
	runtime.GC()
	fmt.Printf("With %T, GC took %s\n", m, timeGC())
	_ = m[0]
}
func mapValue(num int) {
	m := make(map[int]int, num)
	for i := 0; i < num; i++ {
		m[i] = i
	}
	runtime.GC()
	fmt.Printf("With %T, GC took %s\n", m, timeGC())
	_ = m[0]
}
func mapPointerShard(num int) {
	shards := make([]map[int]*int, 100)
	for i := range shards {
		shards[i] = make(map[int]*int)
	}
	for i := 0; i < num; i++ {
		shards[i%100][i] = &i
	}
	runtime.GC()
	fmt.Printf("With map shards (%T), GC took %s\n", shards, timeGC())
	_ = shards[0][0]
}
func mapValueShard(num int) {
	shards := make([]map[int]int, 100)
	for i := range shards {
		shards[i] = make(map[int]int)
	}
	for i := 0; i < num; i++ {
		shards[i%100][i] = i
	}
	runtime.GC()
	fmt.Printf("With map shards (%T), GC took %s\n", shards, timeGC())
	_ = shards[0][0]
}
const N = 5e7 // 5000w
func BenchmarkMapPointer(b *testing.B) {
	mapPointer(N)
}
func BenchmarkMapValue(b *testing.B) {
	mapValue(N)
}
func BenchmarkMapPointerShard(b *testing.B) {
	mapPointerShard(N)
}
func BenchmarkMapValueShard(b *testing.B) {
	mapValueShard(N)
}
複製代碼 
 運行測試
$ go test -bench=^BenchmarkMapPointer$ -benchmem With map[int]*int, GC took 545.139836ms goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMapPointer-4 1 9532798100 ns/op 1387850488 B/op 724960 allocs/op $ go test -bench=^BenchmarkMapPointerShard$ -benchmem With map shards ([]map[int]*int), GC took 688.39764ms goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMapPointerShard-4 1 20670458639 ns/op 4286763416 B/op 1901279 allocs/op $ go test -bench=^BenchmarkMapValueShard$ -benchmem With map shards ([]map[int]int), GC took 1.965519ms goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMapValueShard-4 1 16190847776 ns/op 4385268936 B/op 1918445 allocs/op $ go test -bench=^BenchmarkMapValue$ -benchmem With map[int]int, GC took 22.993926ms goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMapValue-4 1 8253025035 ns/op 1444338752 B/op 724512 allocs/op 複製代碼
GODEBUG=gctrace=1 
 $ GODEBUG=gctrace=1 go test -bench=^BenchmarkMapPointer$ -benchmem ... gc 3 @0.130s 19%: 0.006+424+0.013 ms clock, 0.027+0.18/424/848+0.055 ms cpu, 1224->1224->1224 MB, 1225 MB goal, 4 P gc 4 @9.410s 2%: 0.005+543+0.002 ms clock, 0.022+0/543/1628+0.011 ms cpu, 1325->1325->1323 MB, 2448 MB goal, 4 P (forced) gc 5 @9.957s 3%: 0.003+547+0.003 ms clock, 0.013+0/547/1631+0.013 ms cpu, 1323->1323->1323 MB, 2647 MB goal, 4 P (forced) With map[int]*int, GC took 550.40821ms 複製代碼
gctrace0.013+0/547/1631+0.013 ms cpu 
 0.0130/547/1631mutator assist54716310.013 
 $ GODEBUG=gctrace=1 go test -bench=^BenchmarkMapValue$ -benchmem ... gc 3 @0.018s 0%: 0.005+0.14+0.015 ms clock, 0.021+0.054/0.020/0.19+0.060 ms cpu, 1224->1224->1224 MB, 1225 MB goal, 4 P gc 4 @8.334s 0%: 0.006+21+0.003 ms clock, 0.027+0/6.4/21+0.013 ms cpu, 1379->1379->1334 MB, 2448 MB goal, 4 P (forced) gc 5 @8.358s 0%: 0.003+19+0.003 ms clock, 0.014+0/5.0/20+0.015 ms cpu, 1334->1334->1334 MB, 2668 MB goal, 4 P (forced) 複製代碼
map 
 string與[]byte的轉換
stringstring[]byte 
 func Example() {
	s := "Hello,world"
	b := []byte(s)
}
複製代碼 
 string[]byte 
 func String2Bytes(s string) []byte {
	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := reflect.SliceHeader{
		Data: stringHeader.Data,
		Len:  stringHeader.Len,
		Cap:  stringHeader.Len,
	}
	return *(*[]byte)(unsafe.Pointer(&bh))
}
func Bytes2String(b []byte) string {
	sliceHeader
	sh := reflect.StringHeader{
		Data: sliceHeader.Data,
		Len:  sliceHeader.Len,
	}
	return *(*string)(unsafe.Pointer(&sh))
}
複製代碼 
 函數返回值使用值,不使用指針
對佔用空間少,頻繁分配的函數,若是函數返回指針,會帶來內存逃逸,使得原來能夠分配在棧(stack)上的內存,須要分配在堆(heap)上。在棧上進行小對象拷貝的性能很好,比分配對象在堆上要好得多。 看下面的例子,2個函數分別返回值,和指針。
type S struct {
	a, b, c int64
	d, e, f string
	g, h, i float64
}
func byCopy() S {
	return S{
		a: 1, b: 1, c: 1,
		e: "lyp", f: "lyp",
		g: 1.0, h: 1.0, i: 1.0,
	}
}
func byPointer() *S {
	return &S{
		a: 1, b: 1, c: 1,
		e: "lyp", f: "lyp",
		g: 1.0, h: 1.0, i: 1.0,
	}
}
複製代碼 
 benchmark函數
func BenchmarkMemoryStack(b *testing.B) {
	var s S
	f, err := os.Create("stack.out")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	err = trace.Start(f)
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		s = byCopy()
	}
	trace.Stop()
	b.StopTimer()
	_ = fmt.Sprintf("%v", s.a)
}
func BenchmarkMemoryHeap(b *testing.B) {
	var s *S
	f, err := os.Create("heap.out")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	err = trace.Start(f)
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		s = byPointer()
	}
	trace.Stop()
	b.StopTimer()
	_ = fmt.Sprintf("%v", s.a)
}
複製代碼 
 運行
go test ./... -bench=BenchmarkMemoryHeap -benchmem -run=^$ -count=10 goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMemoryHeap-4 19625536 53.0 ns/op 96 B/op 1 allocs/op go test ./... -bench=BenchmarkMemoryStack -benchmem -run=^$ -count=10 goos: darwin goarch: amd64 pkg: com.learn/gormLearn/gc_gc BenchmarkMemoryStack-4 163253341 7.22 ns/op 0 B/op 0 allocs/op 複製代碼
7.22 ns/op53.0 ns/op 
 使用struct{}優化
struct{} 
 func assign(num int) {
	m := make(map[int]bool, num)
	for i := 0; i < num; i++ {
		m[i] = true
	}
}
func assignStruct(num int) {
	m := make(map[int]struct{}, num)
	for i := 0; i < num; i++ {
		m[i] = struct{}{}
	}
}
複製代碼 
 struct{} 
 GC分析的工具
- go tool pprof
 - go tool trace
 - go build -gcflags=」-m」
 - GODEBUG=」gctrace=1」
 
個人公衆號:lyp分享的地方