Murmurhash-go源码阅读
源码地址:
https://github.com/spaolacci/murmur3
如何理解它说的原生go实现:底层实现不依赖c和c++的库,只用go的标准库实现。
分析一下TestRefStrings中的对于128位的murmur3算法进行分析
// New128WithSeed returns a 128-bit hasher set with explicit seed value
func New128WithSeed(seed uint32) Hash128 {d := new(digest128)d.seed = seedd.bmixer = d // 这里有点奇怪d.Reset()return d
}type digest128 struct {digesth1 uint64 // Unfinalized running hash part 1.h2 uint64 // Unfinalized running hash part 2.
}type digest struct {clen int // Digested input cumulative length.tail []byte // 0 to Size()-1 bytes view of `buf'.buf [16]byte // Expected (but not required) to be Size() large.seed uint32 // Seed for initializing the hash.bmixer
}
// 另外digest重写了bmixer的函数,先省略,等用的时候看。type bmixer interface {bmix(p []byte) (tail []byte)Size() (n int)reset()
}type Hash128 interface {hash.HashSum128() (uint64, uint64)
}
看下这一段代码,很有意思,用var来保证digest128实现了所有的方法。
// Make sure interfaces are correctly implemented.
var (_ hash.Hash = new(digest128)_ Hash128 = new(digest128)_ bmixer = new(digest128)
)
可以看到我们创建一个hasher,type=interface, 然后实现了各种各样的function。
关于
d.bmixer = d
这一行代码,这么写,猜测只是用来实现bmixer的方法。方便digest128 重写一系列函数。
murmur128.go
func (d *digest128) bmix(p []byte) (tail []byte) {//...
}
//等
总之,我们现在new得到了一个hasher,并且做了一些初始化工作:
func (d *digest) Reset() {d.clen = 0d.tail = nild.bmixer.reset()
}
func (d *digest128) reset() { d.h1, d.h2 = uint64(d.seed), uint64(d.seed)
}
这里可以看到digest中bmixer里面的digest指针指向的就是digest本身。。。这是个俄罗斯套娃。
接着把需要hash的string写进去:
h128.Write([]byte(elem.s))
更新了digest中的buf、tail、clen等内容。然后就进行hash
v1, v2 := h128.Sum128()func (d *digest128) Sum128() (h1, h2 uint64) {h1, h2 = d.h1, d.h2var k1, k2 uint64switch len(d.tail) & 15 { case 15:k2 ^= uint64(d.tail[14]) << 48fallthroughcase 14:k2 ^= uint64(d.tail[13]) << 40fallthroughcase 13:k2 ^= uint64(d.tail[12]) << 32fallthroughcase 12:k2 ^= uint64(d.tail[11]) << 24fallthroughcase 11:k2 ^= uint64(d.tail[10]) << 16fallthroughcase 10:k2 ^= uint64(d.tail[9]) << 8fallthroughcase 9:k2 ^= uint64(d.tail[8]) << 0k2 *= c2_128k2 = bits.RotateLeft64(k2, 33)k2 *= c1_128h2 ^= k2fallthroughcase 8:k1 ^= uint64(d.tail[7]) << 56fallthroughcase 7:k1 ^= uint64(d.tail[6]) << 48fallthroughcase 6:k1 ^= uint64(d.tail[5]) << 40fallthroughcase 5:k1 ^= uint64(d.tail[4]) << 32fallthroughcase 4:k1 ^= uint64(d.tail[3]) << 24fallthroughcase 3:k1 ^= uint64(d.tail[2]) << 16fallthroughcase 2:k1 ^= uint64(d.tail[1]) << 8fallthroughcase 1:k1 ^= uint64(d.tail[0]) << 0k1 *= c1_128k1 = bits.RotateLeft64(k1, 31)k1 *= c2_128h1 ^= k1}h1 ^= uint64(d.clen)h2 ^= uint64(d.clen)h1 += h2h2 += h1h1 = fmix64(h1)h2 = fmix64(h2)h1 += h2h2 += h1return h1, h2
}func fmix64(k uint64) uint64 {k ^= k >> 33k *= 0xff51afd7ed558ccdk ^= k >> 33k *= 0xc4ceb9fe1a85ec53k ^= k >> 33return k
}
golang 里面的switch case 满足其中一条分支的话会选择其分支执行,执行完毕会自动break,但fallthrough指令会强制执行下一个分支。
注意位运算的^和&
算法思路:
1 拿string的len & 15, 其实就是对16取膜运算。
拿前7位分别左移48、40、32、24、16、8、0位然后一起做或运算。得到k2. 然后用k2✖️一个很大的数,然后左移33位,然后再乘一个很大的数得到h1.
拿后8位分别左移56、48、40、32、24、16、8、0位然后一起做或运算,✖️一个很大的数,左移31位然后在✖️一个很大的数得到k1.
然后对string.len 取或运算。
互相加
最后得到h1和h2.
参考一下64和32位的实现
func (d *digest64) Sum64() uint64 {h1, _ := (*digest128)(d).Sum128()return h1
}func (d *digest32) Sum32() (h1 uint32) {h1 = d.h1var k1 uint32switch len(d.tail) & 3 {case 3:k1 ^= uint32(d.tail[2]) << 16fallthroughcase 2:k1 ^= uint32(d.tail[1]) << 8fallthroughcase 1:k1 ^= uint32(d.tail[0])k1 *= c1_32k1 = bits.RotateLeft32(k1, 15)k1 *= c2_32h1 ^= k1}h1 ^= uint32(d.clen)h1 ^= h1 >> 16h1 *= 0x85ebca6bh1 ^= h1 >> 13h1 *= 0xc2b2ae35h1 ^= h1 >> 16return h1
}
所以murmur快是因为位运算,那为什么这一堆位运算能够使碰撞低呢。
参考一下:https://kknews.cc/zh-hk/code/naglky2.html
核心就是不断进行左移,至于某些const数字为什么是那个值,是通过计算、实验得出来的。。。然后具体的也没资料说,难受啊。
Murmurhash-go源码阅读相关推荐
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- centos下将vim配置为强大的源码阅读器
每日杂事缠身,让自己在不断得烦扰之后终于有了自己的清静时光来熟悉一下我的工具,每次熟悉源码都需要先在windows端改好,拖到linux端,再编译.出现问题,还得重新回到windows端,这个过程太耗 ...
- 源码阅读:AFNetworking(十六)——UIWebView+AFNetworking
该文章阅读的AFNetworking的版本为3.2.0. 这个分类提供了对请求周期进行控制的方法,包括进度监控.成功和失败的回调. 1.接口文件 1.1.属性 /**网络会话管理者对象*/ @prop ...
- 源码阅读:SDWebImage(六)——SDWebImageCoderHelper
该文章阅读的SDWebImage的版本为4.3.3. 这个类提供了四个方法,这四个方法可分为两类,一类是动图处理,一类是图像方向处理. 1.私有函数 先来看一下这个类里的两个函数 /**这个函数是计算 ...
- mybatis源码阅读
说下mybatis执行一个sql语句的流程 执行语句,事务等SqlSession都交给了excutor,excutor又委托给statementHandler SimpleExecutor:每执行一次 ...
- 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment
24 UsageEnvironment使用环境抽象基类--Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类--Live555源码阅读 ...
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- 源码阅读笔记 BiLSTM+CRF做NER任务 流程图
源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...
- 源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
该文章阅读的AFNetworking的版本为3.2.0. AFAutoPurgingImageCache该类是用来管理内存中图片的缓存. 1.接口文件 1.1.AFImageCache协议 这个协议定 ...
- webpack源码阅读——npm脚本运行webpack与命令行输入webpack的区别
原文地址:webpack源码阅读--npm脚本执行webpack与命令行输入webpack执行的区别 如有错误,欢迎指正! webpack是目前被大家广为使用的模块打包器.从命令行输入webpack或 ...
最新文章
- 其他算法-PCA主成分分析
- 隐式反馈的去噪,模型取得巨大提升
- linux下安装 QQ(wine qq 2013-2014)
- python 银行业务系统程序编程写_python多线程实现代码(模拟银行服务操作流程)
- 转:程序员每天该做的事
- java对象深克隆_JAVA中对象的克隆及深拷贝和浅拷贝
- 2021年五一杯数学建模A题(疫苗生产调度问题)详细分析
- thread类_Python线程:thread对象
- javascript闭包续
- idea 安装uml 画图工具
- L1-064 估值一亿的AI核心代码 (20 分)
- 【移动端】企业微信移动app测试实战(2)、(3)
- Unity摄像头仿真调研(svl)
- 宝塔一键安装php,宝塔管理面板一键安装Tipask3.5版本教程
- 百度搜索结果显示“我喜欢”按钮
- 计算机曝光模式有哪些,曝光模式_拍摄技巧_太平洋电脑网PConline
- 360安全卫士造成网站不能访问的解决办法
- 拼多多商家如何虚假发货的情况出现?
- Office WORD如何简繁转换
- ros中启动rviz显示段错误,核心以转储问题 rviz process has died
热门文章
- Shopify Liquid 日期
- Python 基于tkinter的GUI编程
- 三菱支持c语言的plc,三菱plc编程用什么语言比较好?三菱编程语言的特点
- 贵州省电子计算机学校,贵州省电子工业学校
- android的wifi直连,WLAN 直连 | Android 开源项目 | Android Open Source Project
- WiFi_Direct 直连开发实战
- 旺旺机器人的快捷短语_千牛工作台设置快捷短语的详细操作方法
- Python拼多多商品详情、拼多多关键字搜索封装成API接口教程
- 【巨杉数据库SequoiaDB】巨杉数据库荣获《金融电子化》“金融科技创新奖”
- 我的计算机专业自学之路