问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

怎么样实现一个较快的Hash Table1

发布网友 发布时间:2024-02-18 14:44

我来回答

1个回答

热心网友 时间:2024-11-20 15:31

我们服务器一直在用boost/sgl stl的hash table,但是从来没有考虑过其中的效率问题,虽然hash_map/unordered_map跑的可能真的比map快一些,可能应该不是你理解的那么快.其实他可以更快一些!!!
  当我自己尝试着实现了一个hash table之后,我发现确实如此.这篇文章也是来说说,如何实现较快的一个.
  通常的hash table都是用开链法,开放地址法来解决冲突.开链法是总容易实现的一个,而且因为效率稳定,被加入了C++11,取名unordered_map.不过效率实在不咋地.
  开放地址法的hash table,我是从google-sparsehash里面注意到的,虽然数据结构,算法导论都会讲到.网上说速度很快,我就去看了一下API,其比普通的unordered_map多了一组API:
1. set_empty_key/set_deleted_key
  在开链法中,所有的节点都是容器内的内容,可是开放地址法中不是的.所以需要额外的信息来维护节点的可用性信息.
  当时我看到这两个API,大概就猜到内存是怎么实现的,闲来无事就是试着写了一个demo,在VC 2008下面跑的结果是,比unordered_map快一倍多;在Linux x64 gcc 4.4下面的结果是,比unordered_map快了将近1倍.
2. 高性能的hash table必须是开放地址法的
  这么说,是有原因的.链表的特性就是容易删除,容易插入.可是hash table不需要这些特性,hash table只需要快.可以链表这东西,偏偏做不到快速定位,虽然你知道有下一个节点,但是你不知道下一个节点的准确位置,经常会造成缓存未命中,浪费大量时间.
3. bucket的容量
  bucket的容量也是影响hash table性能的一个因素.无数的数据结构和算法书籍,都教导大家,通过质数取余数,可以获得比较好的下标分布.可是,无论是除法还是乘法,消耗都是相当高的.十几个或者几十个时钟周期,始终比不上一两个时钟周期快.所以,高性能的hash table必须要把bucket的容量设置成2^n.google-sparsehash里面初始容量是32.扩容的话,都是直接左移;算下标的话,都是(容量-1) & hash_value,简单的一个位运算搞定.
4. 正确实现find_position
  我自己实现的hash table,是线性探测法的.所以find position也是比较简单,就是通过hash value和掩码,获取到其实下标,然后一个一个test.需要把buckets当作是环形的,否则buckets最末位的数据冲突就会不好搞.(我当时没有考虑这一点,直接给他扩容了.....)
5. 对象模型
  不同的Key和Value模型,可以导致你对Hash Table的不同实现.简单的说,在C里面,你可以不用考虑Key和Value的生命周期(:D),但是C++里面,你不得不考虑Key,Value的生命周期问题.你不能做一个假设,key和value都是简单数据类型.一个int映射到一个对象,这种经常会用到的.
  所以,erase一个key的时候,需要把key设置成deleted,然后还要把value重置一遍.如果没有重置,对象所引用的内存有可能就会被泄露.
  这引发了我另外一个想法,就是通过模板,来特化Value的reset行为.因为不是所有的Value都是需要被重置的,只有那些复杂对象,才需要.
6. 可以考虑缓冲hash value
  如果key都是简单数据,而非string或者复杂的数据类型,缓冲是没有任何意义的,因为hash value可以被快速的计算出来;但是当key是char*,或者一些复杂的数据类型,缓冲就会变的有意义.而且缓冲更有利于重排,容器扩容的时候速度会更快一些.
7. 考虑使用C的内存分配器
  尽量不要使用C++的new/delete来分配内存.new,delete会有对象的构造,析构过程,这可能不是你所希望的.针对key和value数据类型的不同,你可能会有自己的特有的构造,析构过程.而且,C的内存分配器,同样可以被一些第三方库优化,比如tcmalloc/jemalloc等.
8. 选一个好的Hash函数(这是最重要的)
9. 尽力防止拷贝
  rehash非常耗时,如果支持C++11,就使用move操作;如果不支持,就用swap,否则会复制很多次.
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
乱字同韵字是什么意思 华硕笔记本电脑触摸板怎么开笔记本电脑触摸板怎么开启和关闭_百度知 ... 陕西职务侵占案立案准则 结婚后我的恋情维系了十年,怎么做到的? 玉米仁子饭产自哪里 中国期货交易所的交易品种有哪些? 历史要怎么读,有啥诀窍 高中历史诀窍 年终会活动策划方案 深度解析:第一财经回放,探索财经新风向 郑州市二七区马寨镇汉堡湾生产主任一手遮天,以权欺人,逼迫员工离职,扣... 四级成绩什么时候发证书 渭南小学校歌歌词 灌篮高手里有一集樱木防守的时候跳出一个人墙来的是第几集啊 战争片第一集日本人杂技团进上仙堡村放毒屠村的叫什么电视?谢谢_百度... 爱琴文明有哪些?1 爱琴文明是什么?12 奥运圣火取火处 所处地区曾在公元前2000年起,形成了爱琴文...5 什么叫爱琴文明?爱琴文明经历了多长时间消灭? 爱琴海文明源于何时9 放太多的东西会导致冰箱不制冷吗? 东直门中医院和北大男科医院那个好 李曰庆和李海松哪个厉害 现在一切都进展顺利翻译 学小语种是不是对英语有一定的要求 学习小语种真的需要英语基础知识吗 学小语种是不是也必须学英语 因挡风玻璃除霜软件缺陷 特斯拉在美国再次召回2.66万辆汽车 怎么取消特斯拉冬季车窗防冻模式 特斯拉车内除霜大概提前多久开 日本国民偶像 山口律子演的一部动作片 前面的那首英文歌叫什么 温岭恒隆商业什么时候开业 ...售价? 观赏鱼(热带鱼)地王三尖价钱,每条多少钱??? QQ308左前轮吃内胎怎么回事? 新疆大学的王牌专业 ...要注册英国VAT,现在VAT到底是一个什么情况啊?怎么注册呢? 水浒传中主要人物的主要事迹!越多越好!明天要答案 卧室客厅超值套餐,只需2万元! 包皮水肿地方一圈黑黑的怎么回事1 男人的包皮上为什么会出现一圈一圈的黑印!这个原因是因为过性生...1 男人包皮发黑是怎么回事6 男人的包皮上为什么会出现一圈一圈的黑印!这个原因是因为过性生... 梦见杂配藜的预兆 ...把除法算式中的除数3抄成了4,得到的商是6,正确的商应该是?_百度知 ... 算24点是很好玩的数学游戏哦!优优拿3、6、2、3四张牌,你能用这四张牌... 在哪里能找到不吃全鱼的典故3 为什么小鱼能吃全吃,不用取内脏?37 为什么最近不要吃淡水鱼?1 有条件的话不要吃春天都是鱼籽的鱼,但是穷人就可以吃,一句古话6 群里发13频道说了,近日不要吃鱼,不要吃 酸菜鱼等等,有这么...10