首页 > 其他分享 >Golang map实现分析

Golang map实现分析

时间:2024-01-23 20:57:46浏览次数:27  
标签:分析 map struct hmap Golang bmap dirty 溢出

数据结构

go的map采用数组+链表形式存储,数据存放于hmap中:

type hmap struct {
    count     int // 哈希表的元素个数,即len()
    flags     uint8  // map状态
    B         uint8  // 2^B为桶的数量
    noverflow uint16 // 溢出桶的数量(预估)
    hash0     uint32 // hash seed

    buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
    oldbuckets unsafe.Pointer // 扩容时用于保存之前的buckets字段
    nevacuate  uintptr        // 下一个迁移的桶编号

    extra *mapextra // 溢出桶
}

type mapextra struct {
    // overflow和oldoverflow保证所有溢出桶的存活,不被gc回收
    overflow    *[]*bmap    // 存放的所有溢出桶的地址
    oldoverflow *[]*bmap    // 存放的所有老的溢出桶的地址

    nextOverflow *bmap      // 指向的下一个溢出桶的地址
}

在buckets中存放的是bmap

// A bucket for a Go map.
type bmap struct {
    tophash [bucketCnt]uint8    // bucketCnt=8
}

//编译期间数据结构
type bmap struct{
    topbits     [8]uint8     //用于表示标志位或hash值高八位来快速定位K/V位置
    keys        [8]keytype
    value       [8]valuetype
    overflow    uintptr    //连接下个bmap溢出桶
}

bmap中仅包含了tophash字段,通过比较不同键的哈希的高8位可以减少访问键值对次数以提高性能。bmap在编译时确定K/V的类型,会存储有key、value、溢出桶信息。每个bmap中最多只会有8个元素,超出部分回连接溢出桶进行存储。

源码分析

初始化

// makemap implements Go map creation for make(map[k]v, hint).
func makemap(t *maptype, hint int, h *hmap) *hmap {
	// 判断申请内存是否超过限制
	mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
	if overflow || mem > maxAlloc {
		hint = 0
	}

	// initialize Hmap
	if h == nil {
		h = new(hmap)
	}
	h.hash0 = fastrand()

	// 如果hint大于8并且hint大于(1<<b)*6.5 就每次增长1
	B := uint8(0)
	for overLoadFactor(hint, B) {
		B++
	}
	h.B = B

	// allocate initial hash table
	// if B == 0, the buckets field is allocated lazily later (in mapassign)
	if h.B != 0 {
		var nextOverflow *bmap
		h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)  // 初始化buckets和溢出桶
		if nextOverflow != nil {
			h.extra = new(mapextra)
			h.extra.nextOverflow = nextOverflow
		}
	}

	return h
}

// overLoadFactor reports whether count items placed in 1<<B buckets is over loadFactor.
func overLoadFactor(count int, B uint8) bool {
	return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)
}

扩容

hmap的扩容会在两种情况下触发:

  • 元素过多:hmap中的元素大于8个并且元素数量大于6.5 * (2 ^B)(见overLoadFactor函数),此时会进行翻倍扩容,即hmap.B+=1
  • 桶过多:B <= 15时,溢出桶的数量大于等于(2 ^ B)B > 15时,溢出桶的数量大于等于(2 ^ 15)(见tooManyOverflowBuckets函数),此时会进行等量扩容,将桶中的元素重新排列,以缩减溢出桶的数量

hmap采用渐进式扩容方式,可以避免一次性扩容带来的性能瞬时抖动。当老桶中的数据还没被迁移时,get就会从老桶中获取。

sync.Map

适合读多更新多但新增少的场景,结构体如下:

// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
type Map struct {
    mu Mutex		// 互斥锁

    // 只读数据,无需加锁
    read atomic.Value // readOnly

    // 访问需要加锁,新添加的key都会先放到dirty中
    dirty map[interface{}]*entry

    // 统计访问read没有未命中然后穿透访问dirty的次数
    misses int
}

// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
    m       map[interface{}]*entry
    amended bool // true if the dirty map contains some key not in m.
}

// An entry is a slot in the map corresponding to a particular key.
type entry struct {
    // If p == nil, the entry has been deleted, and either m.dirty == nil or m.dirty[key] is e.
    //
    // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry is missing from m.dirty.
    //
    // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty != nil, in m.dirty[key].
    p unsafe.Pointer // *interface{}
}

sync.Map会把读和写的数据分离存储,读取数据时先尝试无锁从read中读取,dirty会记录新增/删除的数据,以减少对read的修改,对于更新操作也有多处使用CAS操作以减少lock操作(见Store方法)。

参考:
go语言之map源码分析
Go 语言设计与实现——哈希表

标签:分析,map,struct,hmap,Golang,bmap,dirty,溢出
From: https://www.cnblogs.com/gxyan/p/17983400

相关文章

  • 每日总结(python文本分析)
    导入文本文档并输出在终端#Python3.x版本importos#获取根目录下文件的绝对路径root_path="./"file_path=os.path.join(root_path,'pinglun.txt')try:#打开文本文件并读取所有内容withopen(file_path,'r',encoding='utf-8')asfile:......
  • 从 fatal 错误到 sync.Map:Go中 Map 的并发策略
    从fatal错误到sync.Map:Go中Map的并发策略原创 波罗学 码途漫漫 2024-01-2121:00 发表于上海 听全文为什么Go语言在多个goroutine同时访问和修改同一个map时,会报出fatal错误而不是panic?我们该如何应对map的数据竞争问题呢?码途漫漫踏实地写......
  • 国产品牌GC6609与TM2209的参数分析,为什么适用于3D打印机,医疗器械等产品中
    步进电机驱动的应用方案目前市场上大多选用国外品牌的电机驱动器,其中trinamic的TMC2208/2209在这一块的应用很广泛。但是由于市场越来越应激。,当前对于产品开发成本要求也越来越低,国产品地准出了相应的TMC2208/2209,因此trinamic的TMC2208/2209已经不具备优势。对于步进驱动方案,在这......
  • 国产品牌GC6609与TM2209的参数分析,为什么适用于3D打印机,医疗器械等产品中
    步进电机驱动的应用方案目前市场上大多选用国外品牌的电机驱动器,其中trinamic的TMC2208/2209在这一块的应用很广泛。但是由于市场越来越应激。,当前对于产品开发成本要求也越来越低,国产品地准出了相应的TMC2208/2209,因此trinamic的TMC2208/2209已经不具备优势。对于步进驱动方案,在这......
  • 软件可行性分析报告
    ......
  • R语言航班延误影响预测分析:lasso、决策树、朴素贝叶斯、QDA、LDA、缺失值处理、k折交
    全文链接:http://tecdat.cn/?p=32760原文出处:拓端数据部落公众号航班延误是航空公司、旅客和机场管理方面都面临的一个重要问题。航班延误不仅会给旅客带来不便,还会对航空公司和机场的运营产生负面影响。因此,对航班延误的影响因素进行预测分析,对于航空公司、旅客和机场管理方面都......
  • 斜45度瓦片地图(Staggered Tiled Map)里的简单数学
    转载至:ShuaiYUAN斜45度瓦片地图(StaggeredTiledMap)里的简单数学前段时间在做游戏的地图编辑功能,我们是在一个斜45度视角的场景上,对地图上的建筑或装饰物进行添加、移动、移除等基本操作,而且位置的改变是以网格作为最小操作单位的。本渣是用StaggeredTiledMap实现的,与垂直视......
  • 浅谈智能照明控制系统应用与节能分析
    引言随着人们的物质和精神生活水平不断提高,对生活的追求向着更舒适、安全、高效和节能的方向发展,对建筑照明技术的要求也越来越高。新兴的“智能照明”技术是现代计算机技术、控制技术、通信技术与建筑照明技术的有机结合,与传统照明技术相比在各方面均具有优越性。但是,该技术在实际......
  • go-carbon v2.3.6 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
    carbon是一个轻量级、语义化、对开发者友好的golang时间处理库,支持链式调用。目前已被awesome-go收录,如果您觉得不错,请给个star吧github.com/golang-module/carbongitee.com/golang-module/carbon安装使用Golang版本大于等于1.16//使用github库goget-ugithu......
  • IBM java的分析工具(ga和ha)学习和整理
    IBMjava的分析工具(ga和ha)学习和整理背景前几天学习了整理了jca工具今天继续学习一下ga工具ga工具主要是分析gclog相关.可以很直观的进行gclog的分析和展示.除了mat之外还有一个比较轻量级的内存dump分析工具ha.想着一起学习和分析一下.ga工具的相关学习下载......