Go中MAP底層原理分析

MAP底層原理分析

參考

  1. https://golang.design/go-questions/map/principal
  2. map | Golang 中文學習文檔

  1. 先來看一下map結構體,(runtime.hmap結構體就是代表著 go 中的map,與切片一樣map的內部實現也是結構體)

    type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.// Make sure this stays in sync with the compiler's definition.count     int // # live cells == size of map.  Must be first (used by len() builtin)flags     uint8B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for detailshash0     uint32 // hash seedbuckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growingnevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)clearSeq   uint64extra *mapextra // optional fields
    }
    

    內部實現圖

在這里插入圖片描述

簡單說明一下各個字段含義

  • count:表示 hamp 中的元素數量,結果等同于len(map)

  • flags : hmap 的標志位,用于表示 hmap 處于什么狀態,有以下幾種可能

    const (iterator     = 1 // 迭代器正在使用桶oldIterator  = 2 // 迭代器正在使用舊桶hashWriting  = 4 // 一個協程正在寫入hmapsameSizeGrow = 8 // 正在等量擴容
    )
    
  • B:hmap 中的哈希桶的數量為1 << B

  • noverflow,hmap 中溢出桶的大致數量。

  • 哈希種子,在 hmap 被創建時指定,用于計算哈希值。

  • buckets,存放哈希桶數組的指針。

  • oldbuckets,存放 hmap 在擴容前哈希桶數組的指針。

  • extra,存放著 hmap 中的溢出桶,溢出桶指的是就是當前桶已經滿了,創建新的桶來存放元素,新創建的桶就是溢出桶。

    type mapextra struct {// 溢出桶的指針切片overflow    *[]*bmap// 擴容前舊的溢出桶的指針切片oldoverflow *[]*bmap// 指向下一個空閑的溢出桶的指針nextOverflow *bmap
    }
    
  1. 一直說的桶是個什么東西 ?

    hamp 中的buckets也就是桶切片指針,在 go 中對應的結構為runtime.bmap,如下所示

    // A bucket for a Go map.
    type bmap struct {tophash [abi.OldMapBucketCount]uint8
    }
    // OldMapBucketCount是如下的這個變量
    // Maximum number of key/elem pairs a bucket can hold.
    OldMapBucketCountBits = 3 // log2 of number of elements in a bucket.
    OldMapBucketCount     = 1 << OldMapBucketCountBits // 默認為8
    

    表面上看,它只有一個字段tophash,不過實際上來說,bmap的字段不止這些,這是因為map可以存儲各種類型的鍵值對,所以需要在編譯時根據類型來推導占用的內存空間,在cmd/compile/internal/reflectdata/reflect.go中的MapBucketType函數的功能就是在編譯時構造 bmap,它會進行一系列檢查工作,比如 key 的類型是否comparable

    // MapBucketType makes the map bucket type given the type of the map.
    func MapBucketType(t *types.Type) *types.Type
    

    bmap內部結構:HOB Hash 指的就是 top hash。

在這里插入圖片描述

實際上bmap結構如下;不過這些字段對我們是不可見的,go 在實際操作中是通過移動 unsafe 指針來進行訪問

// 實際運行時定義(簡化為可理解的結構)
type bmap struct {// 哈希值高8位數組 (每個槽位1字節)tophash [abi.OldMapBucketCount]uint8  // bucketCnt = 8// 以下是隱式字段(編譯器在編譯時根據鍵值類型動態生成內存布局)keys    [abi.OldMapBucketCount]keyType   // 鍵數組(連續存儲)elems   [abi.OldMapBucketCount]elemType  // 值數組(連續存儲)注意:1.17+ 改名為 elems// 溢出桶指針(重要:在 keys/elems 之后存儲)overflow *bmap
}
// 注意:有些文章還有pad字段 ,該字段是為了保持內存對齊,提高一些操作的效率的

tophash字段是什么 ?

首先,根據字面意思就是一個為uint8類型的長度為 8的數組 ,此時也就定義了 一個桶中可以存放8個鍵值對,桶中每個元素是uint8,代表每個鍵(key)對應的hash值得高八位, 用于定位該鍵(key)在本桶的位置。

另外,tophash還有一些特殊的值,通常是處于擴容的遷移狀態下:

const (emptyRest      = 0 // 當前元素是空的,并且該元素后面也沒有可用的鍵值了emptyOne       = 1 // 當前元素是空的,但是該元素后面有可用的鍵值。evacuatedX     = 2 // 擴容時出現,只能出現在oldbuckets中,表示當前元素被搬遷到了新哈希桶數組的上半區evacuatedY     = 3 // 擴容時出現只能出現在oldbuckets中,表示當前元素被搬遷到了新哈希桶數組的下半區evacuatedEmpty = 4 // 擴容時出現,元素本身就是空的,在搬遷時被標記minTopHash     = 5 // 對于一個正常的鍵值來說tophash的最小值
)
  • minTopHash:當一個 cell(每個tophash元素抽象成每一個cell) 的 tophash 值小于 minTopHash 時,標志這個 cell 的遷移狀態。因為這個狀態值是放在 tophash 數組里,為了和正常的哈希值區分開,會給 key 計算出來的哈希值一個增量:minTopHash。這樣就能區分正常的 top hash 值和表示狀態的哈希值
  • 其余變量是遷移過程中用到的 ,后續說明
  1. key定位過程

    參考文章中說明的很好:


    key 經過哈希計算后得到哈希值,共 64 個 bit 位(64位機,32位機就不討論了,現在主流都是64位機),計算它到底要落在哪個桶時,只會用到最后 B 個 bit 位。還記得前面提到過的 B 嗎?如果 B = 5,那么桶的數量,也就是 buckets 數組的長度是 2^5 = 32。

    例如,現在有一個 key 經過哈希函數計算后,得到的哈希結果是:

     10010111 | 000011110110110010001111001010100010010110010101010 │ 01010
    

    用最后的 5 個 bit 位,也就是 01010,值為 10,也就是 10 號桶。這個操作實際上就是取余操作,但是取余開銷太大,所以代碼實現上用的位操作代替。

    再用哈希值的高 8 位,找到此 key 在 bucket 中的位置,這是在尋找已有的 key。最開始桶內還沒有 key,新加入的 key 會找到第一個空位,放入。

在這里插入圖片描述

上圖中,假定 B = 5,所以 bucket 總數就是 2^5 = 32。首先計算出待查找 key 的哈希,使用低 5 位 00110,找到對應的 6 號 bucket,使用高 8 位 10010111,對應十進制 151,在 6 號 bucket 中尋找 tophash 值(HOB hash)為 151 的 key,找到了 2 號槽位,這樣整個查找過程就結束了。

如果在 bucket 中沒找到,并且 overflow 不為空,還要繼續去 overflow bucket 中尋找,直到找到或是所有的 key 槽位都找遍了,包括所有的 overflow bucket。


keyvalue的定位方法

// key 定位公式
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))// value 定位公式
e := add(unsafe.Pointer(b), dataOffset+abi.OldMapBucketCount*uintptr(t.KeySize)+i*uintptr(t.ValueSize))

b 是 bmap 的地址,這里 bmap 還是源碼里定義的結構體,只包含一個 tophash 數組,經編譯器擴充之后的結構體才包含 key,value,overflow 這些字段。dataOffset 是 key 相對于 bmap 起始地址的偏移, 也就是bmap中key地址開始的位置:

dataOffset = unsafe.Offsetof(struct {b bmapv int64}{}.v)

當定位到一個具體的 bucket 時,里層循環就是遍歷這個 bucket 里所有的 cell,或者說所有的槽位,也就是 bucketCnt=8 個槽位。整個循環過程:

在這里插入圖片描述

遍歷過程中會判斷每一個cell的狀態 ,判斷這個bucket是否搬遷完畢,源碼中用到的函數

func evacuated(b *bmap) bool {h := b.tophash[0]return h > empty && h < minTopHash
}

只取了 tophash 數組的第一個值,判斷它是否在 0-4 之間。對比上面的常量,當 top hash 是 evacuatedEmptyevacuatedXevacuatedY 這三個值之一,說明此 bucket 中的 key 全部被搬遷到了新 bucket。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82559.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82559.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82559.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

#開發環境篇:postMan可以正常調通,但是瀏覽器里面一直報403

本地header代理下面內容即可 headers: { // 添加必要的請求頭 ‘Host’: ‘服務端域名’, ‘Origin’: https://服務端域名, ‘Referer’: https://服務端域名 }, devServer: {// 本地開發代理API地址proxy: {^/file: {target: https://服務端域名,changeOrigin: true, // 是否…

【論文閱讀 | PR 2024 |ICAFusion:迭代交叉注意力引導的多光譜目標檢測特征融合】

論文閱讀 | PR 2024 |ICAFusion&#xff1a;迭代交叉注意力引導的多光譜目標檢測特征融合 1.摘要&&引言2.方法2.1 架構2.2 雙模態特征融合&#xff08;DMFF&#xff09;2.2.1 跨模態特征增強&#xff08;CFE&#xff09;2.2.2 空間特征壓縮&#xff08;SFS&#xff09;…

效率、便捷、安全:智慧充電樁一站式解決方案如何重塑新能源充電體驗?

在新能源浪潮席卷全球的背景下&#xff0c;電動汽車的普及對充電基礎設施提出了更高要求。傳統充電模式因效率低、操作繁瑣、安全隱患等問題&#xff0c;難以滿足用戶需求。智慧充電樁一站式解決方案應運而生&#xff0c;通過技術創新將效率、便捷與安全融為一體&#xff0c;徹…

杰發科技AC7840——Timer修改重裝載值

需要在運行過程中修改定時器的中斷時間 int main(void) {SystemClock_Config(); /*時鐘初始化*/GPIO_LedInit(); /*GPIO初始化*/TIMER_Init(); /*定時器初始化*/InitDebug(); …

https和http有什么區別-http各個版本有什么區別

http和 https的區別 HTTP&#xff08;超文本傳輸協議&#xff09;和 HTTPS&#xff08;安全超文本傳輸協議&#xff09;是兩種用于在網絡上傳輸數據的協議&#xff0c;它們的主要區別在于安全性&#xff1a; HTTP&#xff08;Hypertext Transfer Protocol&#xff09;&#x…

低秩矩陣、奇異值矩陣和正交矩陣

低秩矩陣 低秩矩陣&#xff08;Low-rank Matrix&#xff09;是指秩&#xff08;rank&#xff09;遠小于其行數和列數的矩陣&#xff0c;即 r a n k ( M ) r ? min ? ( m , n ) rank(M) r \ll \min(m,n) rank(M)r?min(m,n)。其核心特點是信息冗余性&#xff0c;可通過少量…

對抗性提示:大型語言模型的安全性測試

隨著大語言模型&#xff08;LLM&#xff09;在虛擬助手、企業平臺等現實場景中的深度應用&#xff0c;其智能化與響應速度不斷提升。然而能力增長的同時&#xff0c;風險也在加劇。對抗性提示已成為AI安全領域的核心挑戰&#xff0c;它揭示了即使最先進的模型也可能被操縱生成有…

SSM 框架核心知識詳解(Spring + SpringMVC + MyBatis)

&#x1f331; 第一部分&#xff1a;Spring 核心原理與使用 1. 什么是 Spring Spring 是一個開源的 Java 企業級開發框架&#xff0c;旨在簡化 Java 企業應用程序開發。它核心思想是控制反轉&#xff08;IoC&#xff09;和面向切面編程&#xff08;AOP&#xff09;&#xff0…

基于 Alpine 定制單功能用途(kiosk)電腦

前言 故事回到 7 年前, 在網上沖浪的時候發現了一篇介紹使用 Ubuntu 打造 kiosk 單功能用途電腦的文章, 挺好玩的, 就翻譯了一下并比葫蘆畫瓢先后用了 CentOS 7, ArchLinux 進行了實現. 歷史文章: 翻譯 - 使用Ubutnu14.04和Chrome打造單功能用途電腦(大屏展示電腦) 使用CentOS…

【機器學習及深度學習】機器學習模型的誤差:偏差、方差及噪聲

機器學習模型的誤差分析 V1.0機器學習模型的衡量準則概念引入機器學習模型誤差分析誤差出現的原因及消除 V1.0 機器學習模型的衡量準則 衡量機器學習模型的好壞可以考慮以下幾個方面&#xff1a; 偏差&#xff08;Bias&#xff09;&#xff1a; 在充分訓練的情況下&#xff0…

混沌映射(Chaotic Map)

一.定義 混沌映射是指一類具有混沌行為的離散時間非線性動力系統&#xff0c;通常由遞推公式定義。其數學形式為 &#xff0c;其中 f 是非線性函數&#xff0c;θ 為參數。它們以簡單的數學規則生成復雜的、看似隨機的軌跡&#xff0c;是非線性動力學和混沌理論的重要研究對象…

多群組部署

相關概念 星形拓撲和并行多組 如下圖&#xff0c;星形組網拓撲和并行多組組網拓撲是區塊鏈應用中使用較廣泛的兩種組網方式。 星形拓撲&#xff1a;中心機構節點同時屬于多個群組&#xff0c;運行多家機構應用&#xff0c;其他每家機構屬于不同群組&#xff0c;運行各自應用…

基于vue3-elemenyui的動態列案例

本案例主要是實現數據模型的解析以及實現el-table的動態列加載。 1.數據結構 公司A\B\C\測試1&#xff0c;是列&#xff0c;功能-url&#xff0c;是行數據&#xff0c;其中功能x是行頭。 this.rawData [{companyName: "公司A",rpWebShows: [{ "功能1": &…

Kerberos面試內容整理-Kerberos 與 LDAP/Active Directory 的集成

Kerberos 通常不會單獨存在于企業環境中,而是與目錄服務相結合以提供完整的身份管理方案。其中,Active Directory (AD) 是 Kerberos 集成應用的典型代表。Active Directory 是微軟的目錄服務,實現了 LDAP(輕量級目錄訪問協議)目錄和 Kerberos 認證的融合。在 AD 域控制器上…

Oracle DG庫控制文件IO錯誤導致宕機的應急處理

Oracle DG庫控制文件IO錯誤導致宕機的應急處理 事故現場偷天換日棋差一招事故現場 一套Oracle 19c DG環境的備庫宕機。 根據告警時間檢查實例宕機時間點附近的alert日志有如下重要信息: 2025-05-25T23:34:10.705385+08:00 KCF: read, write or open error, block=0x3377ee …

《前端面試題:前端盒模型》

前端盒模型完全指南&#xff1a;從原理到面試實戰 &#x1f381; 端午快樂&#xff01; 各位前端小伙伴&#xff0c;端午節快樂&#xff01;&#x1f96e; 在這個粽葉飄香的時節&#xff0c;愿你的代碼如龍舟般一往無前&#xff0c;bug 如咸蛋黃般被完美包裹&#xff01;今天我…

BERT:讓AI真正“讀懂”語言的革命

BERT&#xff1a;讓AI真正“讀懂”語言的革命 ——圖解谷歌神作《BERT: Pre-training of Deep Bidirectional Transformers》 2018年&#xff0c;谷歌AI團隊扔出一篇核彈級論文&#xff0c;引爆了整個NLP領域。這個叫BERT的模型在11項任務中屠榜&#xff0c;甚至超越人類表現…

爬蟲入門:從基礎到實戰全攻略

&#x1f9e0; 一、爬蟲基礎概念 1.1 爬蟲定義 爬蟲&#xff08;Web Crawler&#xff09;是模擬瀏覽器行為&#xff0c;自動向服務器發送請求并獲取響應數據的一種程序。主要用于從網頁中提取結構化數據&#xff0c;供后續分析、展示或存儲使用。 1.2 爬蟲特點 數據碎片化&…

uni-app學習筆記二十一--pages.json中tabBar設置底部菜單項和圖標

如果應用是一個多 tab 應用&#xff0c;可以通過 tabBar 配置項指定一級導航欄&#xff0c;以及 tab 切換時顯示的對應頁。 在 pages.json 中提供 tabBar 配置&#xff0c;不僅僅是為了方便快速開發導航&#xff0c;更重要的是在App和小程序端提升性能。在這兩個平臺&#xff…

行業分析---小米汽車2025第一季度財報

1 背景 最近幾年是新能源汽車的淘汰賽&#xff0c;前短時間比亞迪再次開始了降價&#xff0c;導致一片上市車企的股價大跌&#xff0c;足見車圈現在的敏感度。因此筆者會一直跟蹤新勢力車企的財報狀況&#xff0c;對之前財報分析感興趣的讀者朋友可以參考以下博客&#xff1a;…