首頁  >  文章  >  後端開發  >  go語言擴容法有哪幾種

go語言擴容法有哪幾種

青灯夜游
青灯夜游原創
2023-01-16 16:11:581521瀏覽

go語言擴容方法有:1、Slice擴容,在使用append向Slice追加元素時,如果Slice空間不足,將會觸發Slice擴容;2、Map擴容。觸發Map擴容的條件有二:1、負載因子大於6.5時,也即平均每個bucket儲存的鍵值對達到6.5個;2、overflow數量大於2^15時,也即overflow數量超過32768時。

go語言擴容法有哪幾種

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

Slice擴容

觸發

使用append向Slice追加元素時,如果Slice空間不足,將會觸發Slice擴容

原理

擴容實際上是重新分配一塊更大的內存,將原Slice資料拷貝進新Slice,然後返回新Slice,擴容後再將資料追加進去。

機制

V1.8之前:

擴充容量的選擇遵循下列規則:

  • 如果原Slice容量小於1024 ,則新Slice容量將擴大為原來的2倍;
  • 如果原Slice容量大於等於1024,則新Slice容量將擴大為原來的1.25倍;
// 1.17及以前的版本中
// old指切片的旧容量, cap指期望的新容量
func growslice(old, cap int) int {
    newcap := old
    doublecap := newcap + newcap
    // 如果期望容量大于旧容量的2倍,则直接使用期望容量作为最终容量
    if cap > doublecap {
        newcap = cap
    } else {
        // 如果旧容量小于1024,则直接翻倍
        if old < 1024 {
            newcap = doublecap
        } else {
            // 每次增长大约1.25倍
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    // 这里忽略了对齐操作
    return newcap
}

V1 .8之後:

新擴容容量的選擇遵循以下規則:(擁有更平滑的擴容係數)

  • 如果原Slice容量小於256,則新Slice容量將擴大為原來的2倍;
  • 如果原Slice容量大於等於256,則新Slice容量將擴大為原來的  新容量= (原容量3*256)/4
// 只关心扩容规则的简化版growslice
func growslice(old, cap int) int {
    newcap := old
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        const threshold = 256 // 不同点1
        if old < threshold {
            newcap = doublecap
        } else {
            for 0 < newcap && newcap < cap {
                newcap += (newcap + 3*threshold) / 4 // 不同点2
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    return newcap
}

Map擴容

觸發擴容的條件有二:

  • 負載因子> 6.5時,也即平均每個bucket儲存的鍵值對達到6.5個。 增量擴容

  • overflow數量 > 2^15時,也即overflow數量超過32768時。 等量擴充功能/重排

  • 注意:建立溢位桶不屬於擴容機制

    #增量擴容

    • 當負載因子過大時,新開啟buckets空間,bucket數量為之前的2 倍
    • 新空間被buckets引用,先前的舊空間被oldbuckets引用
    • 之後逐漸將oldbuckets中的資料搬遷到新開闢的buckets空間中去

    考慮到如果map儲存了數以億計的key -value,一次搬遷將會造成比較大的延時,Go採用逐步搬遷策略,即每次訪問map時都會觸發一次搬遷,每次搬遷2個鍵值對當oldbuckets中的鍵值對全部搬遷完畢後,刪除oldbuckets。

    下圖展示了包含一個bucket滿載的map(為了描述方便,圖中bucket省略了value區域):

    ##目前map儲存了7個鍵值對,只有1個bucket。此時負載因子為7 > 6.5。再次插入資料時將會觸發

    擴容操作,擴容之後再將新插入鍵寫入新的bucket。注意,因為負載因子的觸發,不是創建溢出桶

    當第8個鍵值對插入時,將會觸發

    擴容擴容後示意圖如下:

    後續對map的存取操作會觸發遷移,並將oldbuckets中的鍵值對逐步的搬遷過來。

    搬遷完成後的示意圖如下:

    資料搬遷過程中原bucket中的鍵值對將存在於新bucket的前面,新插入的鍵值對將存在於新bucket的後面。

    等量擴容/重排

    所謂等量

    擴容,其實並不是擴大容量,buckets數量不變,重新做一遍類似增量擴容的搬遷​​動作,把鬆散的鍵值對重新排列一次,以使bucket的使用率更高,進而保證更快的訪問。 在極端場景下,例如不斷地增刪,而鍵值對正好集中在一小部分的bucket,這樣會造成overflow的bucket數量增多,但負載因子又不高,從而無法執行增量搬遷的情況,如下圖所示:

    上圖可見,overflow的bucket大部分是空的,存取效率會很差。此時進行一次等量

    擴容,即buckets數量不變,經過重新組織後overflow的bucket數量會減少,即節省了空間又會提高訪問效率。

    【相關推薦:

    Go影片教學程式設計教學

    以上是go語言擴容法有哪幾種的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn