BruceChen7 / gitblog

My blog
6 stars 1 forks source link

golang性能优化 #47

Open BruceChen7 opened 1 year ago

BruceChen7 commented 1 year ago

参考资料

(usage::切片的性能和陷阱)

a := [3]int{1, 2, 3}
b := [4]int{2, 4, 5, 6}
a = b // cannot use b (type [4]int) as type [3]int in assignment

陷阱

for 和 range 的性能比较

(usage::计算结构体占用空间 )

package main
import (
    "fmt"
    "unsafe"
)
type Args struct {
    num1 int
    num2 int
}
type Flag struct {
    num1 int16
    num2 int32
}

func main() {
    fmt.Println(unsafe.Sizeof(Args{})) // 16
    fmt.Println(unsafe.Sizeof(Flag{})) // 8
}

如何图形化看 pading

package main

import (
    "fmt"
    "unsafe"
)

type memAlign struct {
    f []string // Sizeof: 24 Alignof: 8 Offsetof: 0
    d string   // Sizeof: 16 Alignof: 8 Offsetof: 24
    b int      // Sizeof: 8  Alignof: 8 Offsetof: 40
    a byte     // Sizeof: 1  Alignof: 1 Offsetof: 48
    c byte     // Sizeof: 1  Alignof: 1 Offsetof: 49
    e byte     // Sizeof: 1  Alignof: 1 Offsetof: 50
}

func main() {
    var m memAlign
    fmt.Println(unsafe.Sizeof(m))
}

空 struct

对时间解析的优化

尽量避免分配

package allocations

import "testing"

func buildOrginalData() []int {
   s := make([]int, 1024)
   for i := range s {
      s[i] = i
   }
   return s
}

func check(v int) bool {
   return v%2 == 0
}

func FilterOneAllocation(data []int) []int {
   var r = make([]int, 0, len(data))
   for _, v := range data {
      if check(v) {
         r = append(r, v)
      }
   }
   return r
}

func FilterNoAllocations(data []int) []int {
   var k = 0
   for i, v := range data {
      if check(v) {
         data[i] = data[k]
         data[k] = v
         k++
      }
   }
   return data[:k]
}

func Benchmark_FilterOneAllocation(b *testing.B) {
   data := buildOrginalData()
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      _ = FilterOneAllocation(data)
   }
}

func Benchmark_FilterNoAllocations(b *testing.B) {
   data := buildOrginalData()
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      _ = FilterNoAllocations(data)
   }
}

// Benchmark_FilterOneAllocation-4  7263 ns/op   8192 B/op  1 allocs/op
// Benchmark_FilterNoAllocations-4   903.3 ns/op    0 B/op  0 allocs/op

Save memory and reduce allocations by combining memory blocks

package allocations

import "testing"

const N = 100

type Book struct {
   Title  string
   Author string
   Pages  int
}

//go:noinline
func CreateBooksOnOneLargeBlock(n int) []*Book {
   books := make([]Book, n)
   pbooks := make([]*Book, n)
   for i := range pbooks {
      pbooks[i] = &books[i]
   }
   return pbooks
}

//go:noinline
func CreateBooksOnManySmallBlocks(n int) []*Book {
   books := make([]*Book, n)
   for i := range books {
      books[i] = new(Book)
   }
   return books
}

func Benchmark_CreateBooksOnOneLargeBlock(b *testing.B) {
   for i := 0; i < b.N; i++ {
      _ = CreateBooksOnOneLargeBlock(N)
   }
}

func Benchmark_CreateBooksOnManySmallBlocks(b *testing.B) {
   for i := 0; i < b.N; i++ {
      _ = CreateBooksOnManySmallBlocks(N)
   }
}

Benchmark_CreateOnOneLargeBlock-4     4372 ns/op  4992 B/op    2 allocs/op
Benchmark_CreateOnManySmallBlocks-4  18017 ns/op  5696 B/op  101 allocs/op

Use value cache pool to avoid some allocations

import "container/list"

var npcPool = struct {
   sync.Mutex
   *list.List
}{
   List: list.New(),
}

func newNPC() *NPC {
   npcPool.Lock()
   defer npcPool.Unlock()
   if npcPool.Len() == 0 {
      return &NPC{}
   }
   return npcPool.Remove(npcPool.Front()).(*NPC)
}

func releaseNPC(npc *NPC) {
   npcPool.Lock()
   defer npcPool.Unlock()
   *npc = NPC{} // zero the released NPC
   npcPool.PushBack(npc)
}

(usage::逃逸)

(usage:: 将 string 改成[]byte)

func StringToSliceByte(s string) []byte {
    l := len(s)
    return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
        Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
        Len:  l,
        Cap:  l,
    }))
}

type/golang #public