【小记】golang_map
map
前言:map 几个操作实现有点复杂,即便之前看懂了没过多久也就忘了,这里简单做下记录。为了便于记忆,将 mapassign 的全过程以流程图的方式展示,其他省略
mapassign

在流程图中可以看到 golang map 在扩容时采取渐进式扩容的策略
mapaccess
- mapaccess1: 用于类似于
val := h[key]只需一个返回值的形式 - mapaccess2: 用于类似于
val, exsit := h[key] - mapaccessK: 用于
for range且特殊时期 - mapaccess_fast<T>: 对 key 为特定类型的优化处理
返回 value 类型的零值
if h == nil || h.count == 0 {
...
return unsafe.Pointer(&zeroVal[0])
}
寻找对应的桶
if c := h.oldbuckets; c != nil {
// 如果存在旧桶数据,说明当前在扩容阶段,且可能查找的数据还未迁移,需要先从旧桶找起
if !h.sameSizeGrow() {
// 一般扩容时,会将容量扩大两倍,因此需要按照之前的数量重新计算哈希值
m >> 1
}
oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
if !evacuated(oldb) {
// 在 mapassign 的流程图中可以看到,迁移是一个桶带着它的溢出桶全部迁移的
// 所以如果该桶未发生迁移,就在当前桶以及其溢出桶查找即可
b = oldb
}
}
该处可以看到 mapaccess 与 mapassign 在扩容时查找数据的方式是有区别的。mapassign 会先迁移数据再去新桶查找,而 mapaccess 会在新旧桶中抉择
mapdelete
空表删键值对
if h == nil || h.count == 0 {
...
return
}
和 mapaccess 一样,对值为 nil 或不含任何键值对的 map 都可以操作。而 mapassign 不能对值为 nil 的 map 操作
重置状态
当成功删除一个键值对后,该 entry 的 tophash 被置为 emptyOne
// 注:进入后面步骤的前提是已经找到了需要删除的键值对,代码已省略
b.tophash[i] = emptyOne
if i == bucketCnt-1 { // 如果是当前桶最后一个 entry
if b.overflow(t) != nil && b.overflow(t).tophash[0] != emptyRest {
// 下一个溢出桶首个 tophash 不为 emptyRest,停止搜索
goto notLast
}
} else {
if b.tophash[i+1] != emptyRest {
// 下一个 entry 的 tophash 不为 emptyRest, 停止搜索
goto notLast
}
}
for {
// 当前 entry 状态为 emptyOne,且后置位状态为 emptyRest,更新当前位置状态
b.tophash[i] = emptyRest
if i == 0 {
// 当前桶以及其后溢出桶都空了
if b == bOrig {
// 若当前桶为第一个桶,代表已经回到初始桶,直接退出即可
break
}
// 寻找前一个桶,并从最后一个 entry 继续
c := b
for b = bOrig; b.overflow(t) != c; b = b.overflow(t) {}
i = bucketCnt - 1
} else {
// 倒序检查之前的 entry 的状态是否需要更改
i--
}
if b.tophash[i] != emptyOne {
// 遇到有存 key/value 的 entry 时结束
break
}
}
mapclear
停止扩容
h.flags &^ = sameSizeGrow // 停止等量扩容
h.oldbuckets = nil // 停止扩容
h.nevacuate = 0 // 停止迁移
清除内存
// makeBucketArray clears the memory pointed to by h.buckets
// and recovers any overflow buckets by generating them
// as if h.buckets was newly alloced.
// If dirtyalloc is nil a new backing array will be alloced and
// otherwise dirtyalloc will be cleared and reused as backing array.
mapclear 会保留正常桶,但是会清除溢出桶以及正常桶桶内的键值对,并且会根据正常桶数量决定是否重新生成溢出桶。也就是说,mapclear 类似于清空数据的等量扩容,但是并不会真的清空所有存储空间。
因此,如果当你想通过清除所有数据来减小 map 的内存占用,应该使用 make 重新赋值
tophash
tophash 不仅记录哈希值的高 8 位,还记录当前位置的状态值,例如:
- 当前位置的 key/value 被删除后,tophash 被置为 emptyOne,若后面的位置都为空,则进一步更新为 emptyRest。当发生哈希冲突时,可以减少比较次数和内存再分配
哈希冲突
解决哈希冲突的方法:
- Open addressing
- Linear probing
- 优点:简单
- 缺点:有 clustering 趋势:当很多冲突发生在同一个哈希值上,周围多个位置就会被填充满,影响其他要插入的元素
- Rehash
- 优点:在满足表大小为素数的条件下可以解决 clustering 问题
- 缺点:需要计算两次
- Quadratic probing
- Linear probing 的变体
- Linear probing
- Chaining
- 优点:不影响其他位置的元素
- 缺点:
- 当很多冲突发生在同一个哈希值上,那么搜索时复杂度就会退化到 O(n),这和 clustering 的影响一致
- 如果链表节点都是从堆中分配,可能造成内存碎片化
补充说明
- 一次迁移操作是将正常桶以及它的溢出桶全部迁移,每次扩容进行 1-2 次这样的操作
- 只有在赋值(mapassign)和删除键值对(mapdelete)时才会继续扩容(如果需要的话),在访问数据(mapaccess)时是不会进行扩容操作的
疑问
map 在判断并发读写上使用 hashWriting 来判断,但这个还是有几率漏判的吧?
【待补充】
可能会补充。。。
- 扩容细节
- 迁移细节
- 应用于 for range 的 iterator
- 对于计算溢出桶的细节没太看懂,没看懂为什么 - 8
【小记】golang_map的更多相关文章
- [原]Paste.deploy 与 WSGI, keystone 小记
Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...
- MySql 小记
MySql 简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...
- Git小记
Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...
- 广州PostgreSQL用户会技术交流会小记 2015-9-19
广州PostgreSQL用户会技术交流会小记 2015-9-19 今天去了广州PostgreSQL用户会组织的技术交流会 分别有两个session 第一个讲师介绍了他公司使用PostgreSQL-X2 ...
- 东哥读书小记 之 《MacTalk人生元编程》
一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...
- Paypal支付小记
Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...
- linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记
上篇文章 小记了: 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记. http://www.cnblogs.com/bleachli/p/4352 ...
- mongodb入门学习小记
Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...
- 【日常小记】统计后缀名为.cc、.c、.h的文件数【转】
转自:http://www.cnblogs.com/skynet/archive/2011/03/29/1998970.html 在项目开发时,有时候想知道源码文件中有多少后缀名为.cc..c..h的 ...
- ulua 路径小记 以及 lua require 机制整理
ulua 路径小记 在学习ulua时,require模块的根路径可以为项目的Lua文件夹或者ToLua文件夹(Editor下),但是在package.path和package.cpath中并没有看到当 ...
随机推荐
- SAP 登录文件路径
链接文件地址C:\Users\Administrator\AppData\Roaming\SAP\Common 复制:Common文件夹所有文件替换
- Windows打开代理快捷方式
最近使用电脑需要频繁打开关闭网络代理(全局代理会导致一些网站打不开),在想有没有快捷方式,一搜果然有,看来有跟我一样的使用习惯的人. 传统切换代理开关,步骤如下: 设置 => 网络和 Inter ...
- 正点原子sys.h文档详解
文档主体内容为位带的映射,目的是实现位带操作. 第一部分:位带映射的宏函数 1 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x200 ...
- K8s集群安全机制
安全机制说明 k8s作为一个分布式集群管理的工具,保证集群的安全性是其一个重要的任务.API Server是集群内部各个组件通信的中介,也是外部控制的入口,,所以K8s的安全机制就是围绕保护API S ...
- soursetree 关于https:git remote: Unauthorized和username和password修改
一.sourcetree推送代码提交不上提示https:git remote: Unauthorized由于没有权限,需要登陆正确的账号以及密码即可以提交 二.SourceTree这是一个无效源路径/ ...
- RIDE,如何指定report,log,output的存放位置
创建测试用例,执行后,report.html,log.html,output.txt 会默认存放到 C:\Users\你的用户名\AppData\Local\Temp下, 例如我的存放在 如果要指定存 ...
- 解决:webpack打包js项目ie11浏览器下报promise 未定义
项目背景:webpack+js+seajs 引入文件用require或者define 1.下载依赖包 npm install babel-polyfill 2.引入该依赖:webpack.conf ...
- 使用svg让页面自适应浏览器大小,整体等比缩放
网页代码: <!DOCTYPE html> <html> <head> <style> body { margin:0; padding:0; widt ...
- CPU性能测试-coremark
测试CPU性能指标会用到 Benchmarks常见的为 Dhrystone 和 CoreMark. CoreMark是一个综合基准,用于测量嵌入式系统中使用的中央处理器(CPU)的性能.它是在2009 ...
- 胖AP组建小型企业WLAN
胖ap组网 无线路由器wrt配置 使用自动连接时,会从无线路由器第一个端口开始连接,但那是接入外网的端口,因此和二层交换机相连的线要手动更改一下,否则和交换机相连的设备无法dhcp获取ip 打开无线路 ...