Golang 優化之路——空結構

寫在前面

開發 hashset 經常使用的套路:javascript

map[int]int8
map[int]bool複製代碼

咱們通常只用 map 的鍵來保存數據,值是沒有用的。因此來緩存集合數據會形成內存浪費。java

空對象

空對象是個神奇的東西。它指的是沒有字段的結構類型。git

type Q struct{}複製代碼

它牛逼的地方在於:github

  • 能夠和普通結構同樣操做緩存

    var a = []struct{}{struct{}{}}
      fmt.Println(len(a)) // prints 1複製代碼
  • 不佔用空間性能

    var s struct{}
      fmt.Println(unsafe.Sizeof(s)) // prints 0複製代碼
  • 聲明兩個空對象,它們指向同一個地址學習

    type A struct{}
      a := A{}
      b := A{}
      fmt.Println(&a == &b) // prints true複製代碼

形成這個結果的緣由是 Golang 的編譯器會把這種空對象都當成runtime.zerobase處理。優化

var zerobase uintptr複製代碼

hashset

有了上面的介紹,就能夠利用空結構來優化 hashset 了。ui

var itemExists = struct{}{}

type Set struct {
    items map[interface{}]struct{}
}

func New() *Set {
    return &Set{items: make(map[interface{}]struct{})}
}

func (set *Set) Add(item interface{}) {
    set.items[item] = itemExists
}

func (set *Set) Remove(item interface{}) {
    delete(set.items, item)
}

func (set *Set) Contains(item interface{}) bool {
    if _, contains := set.items[item]; !contains {
        return false
    }
    return true
}複製代碼

一個簡易的 hashset 實現就完成了。spa

性能比較

func BenchmarkIntSet(b *testing.B) {
    var B = NewIntSet(3)
    B.Set(10).Set(11)
    for i := 0; i < b.N; i++ {
        if B.Exists(1) {

        }
        if B.Exists(11) {

        }
        if B.Exists(1000000) {

        }
    }
}

func BenchmarkMap(b *testing.B) {
    var B = make(map[int]int8, 3)
    B[10] = 1
    B[11] = 1
    for i := 0; i < b.N; i++ {
        if _, exists := B[1]; exists {

        }
        if _, exists := B[11]; exists {

        }
        if _, exists := B[1000000]; exists {

        }
    }
}

BenchmarkIntSet-2       50000000                35.3 ns/op             0 B/op          0 allocs/op
BenchmarkMap-2          30000000                41.2 ns/op             0 B/op          0 allocs/op複製代碼

結論

  • 性能,有些提高,但不是特別明顯。尤爲是線上壓力不大的狀況性能應該不會有明顯變化;
  • 內存佔用。咱們的服務緩存較多、佔用內存較大,經過這個優化實測能夠減小 1.6 GB 的空間。不過這個優化的空間取決於數據量。

參考文獻

相關文章
相關標籤/搜索