[论文笔记]multi-copy-cuckoo


事实上写的时候完全没想到会这么长,反而有点不想看orz
如何在500w个单词中统计特定前缀的单词有多少个?
如何判断一个数是否在40亿个整数中?

Introduction

chaining method和线性探查需要额外的时间和资源来解决冲突,达不到最佳的插入和查找性能bound。
Cuckoo hashing使用d个hash函数给每个item提供备用的桶,核心两个词:multiple hashing 和 relocation,帮助该算法实现很高的load ratio,在最坏的情况下最多要查找d个buckets去找某个item(O(1))。由于优秀的查找性能,经常被用在存储系统、数据库、privacy & security, 网络、架构、数据处理。
但是良好的查找性能是以插入复杂的冲突处理为代价的。多次重插后也可能还是失败。

对传统Cuckoo hashing着手改进的三个点

总体来说有三个因素影响了Cuckoo hashing的性能:

  • 插入时的循环踢出。由于标准Cuckoo hashing不能预测哪个item有空的备用buckets,只能通过BFS或者是随机的方式去决策。会浪费时间不说,还可能造成死循环。A good strategy should find a solution fast if such solution exists
  • 插入失败的损耗要尽可能小。传统的cuckoo hashing会rehash,把所有插入的读出来,重新算一个hash函数,然后再放入一个更大的表。一个更靠谱的补救措施是分配一个额外的小空间存所有插入失败的items。但是如果在主表里没有查找到,还是要到stash里面再找。额外的查找不仅影响查找项目,能使也限制了主表的最大可承载数。要最大化地利用stash,必须减少在stash中查找的消耗,提高可扩展性以及减少没必要的checking
  • 一个循环中多桶检查:查找一个item的时候,所有的备用桶都需要访问,影响了查找性能,尤其在表很大,需要把表放在外部内存的情况下。narrow down the subset of buckets that may contain the item beforehand and optimize the accessing pattern

已有的方法是怎么改进的

maxloop 防止死循环。
  • 尝试预先判断出循环次数,从一开始就不允许进入死循环:SmartCuckoo[15]、Necklace[16]
  • 减少踢出过程中的重复部分,使得更多的buckets被搜索到,搜索到空桶的概率也会增加:MinCounter[17]
  • 每个桶分配多个槽:blocked Cuckoo hash tables(BCHT)[18][19][20][21]
插入失败
  • 将stash on-chip?减少对性能的影响,当stash本身满的时候,其中的items会尝试插入主表When the stash itself is full, items stored in it will take a try to the main table until some space is freed. A small stash of size 4 is regarded as enough to achieve rather high load (for example 95% in [24]) with high probability.?:Cuckoo hashing with a stash (CHS)[22] propose
    已经很高了,好奇如何提升emm
多桶查询

当实施的平台上要用很小很快的on-chip memory去处理很大的表,(for example the ASIC/FPGA/SOC based packet processing devices).每次要检查多个位置就会成为大问题。

  • 使用可以放在on-chip内存里的compact helping structure例如 bloom filter来pre-screening,来减少不必要的对主表的访问:DEHT[25],EMOMA[24]

本文的方案

  • multiple copies of item,将item同时放入所有可用桶中。不用随机选择一个可用桶插入 通过冗余度可以很清楚地知道冲突的时候替换谁是最优方案,加速了插入速度,也避免了死循环。Keeping copies in all the available candidate buckets will maintain the flexibility and avoid entering the sub-optimal situation early: the optimal placement will come out naturally later on when the other occupied buckets are appropriately given away as per request to new items, who turn out to be the better owners of these buckets in an overall optimal arrangement.
  • 每个桶设置了一个计数器来跟踪存储的item有多少个备份。只要备份数大于1,必要的时候就可以被overwritten。同一个item的所有bucket的计数器值都一样。
  • 查找的时候,由于同一个item的所有桶的计数器应该一样,这个特点可以被用于排查掉不可能的情况。比如其中一个桶的c值是0,那么该item一定不存在。Furthermore, because an item can always overwrite a redundant copy to settle down, if a lookup fails with any candidate bucket having counter value larger than 1, we know that item must have not been inserted before and skip checking the stash. These 通过这些观察,可以避免查询一些bucket,或者是对stash的不必要检查。
  • McCuckoo特别适合于主表只能放在慢一点的二级存储上,上述三个问题可以被统一解决。为了最大化counters的好处,要把它们放在on-chip embedded memory. a compact on-chip counter array。操作逻辑也很简单。

本文的贡献:

  • 引入了multi-copy到Cuckoo-based hashing中,减少了relocation的盲目性,提升了bucket的可用性,以及提高了插入速度和正确率
  • 提出一种新的compact on-chip helping structure减少对off-chip memory的不必要访问
  • 一种有效的pre-screening机制减少对stash的查询

关于hash

rehash

当使用二度哈希时,重要的是在执行了 hashsize 次探查后,哈希表中的每一个位置都有且只有一次被访问到。也就是说,对于给定的 key,对哈希表中的同一位置不会同时使用 Hi 和 Hj。在 Hashtable 类中使用二度哈希公式,其始终保持 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1)) 与 hashsize 互为素数(两数互为素数表示两者没有共同的质因子)。
向 Hashtable 中添加新元素时,需要检查以保证元素与空间大小的比例不会超过最大比例。如果超过了,哈希表空间将被扩充。

步骤如下:

  • 哈希表的位置空间几乎被翻倍。准确地说,位置空间值从当前的素数值增加到下一个最大的素数值。
  • 因为二度哈希时,哈希表中的所有元素值将依赖于哈希表的位置空间值,所以表中所有值也需要重新二度哈希

由此看出,对哈希表的扩充将是以性能损耗为代价。因此,我们应该预先估计哈希表中最有可能容纳的元素数量,在初始化哈希表时给予合适的值进行构造,以避免不必要的扩充。

chaining

在这里插入图片描述
使用链接技术添加元素的操作涉及到哈希计算和链表操作,但其仍为常量,渐进时间为 O(1)。而进行查询和删除操作时,其平均时间取决于元素的数量和桶(bucket)的数量。具体的说就是运行时间为 O(n/m),这里 n 为元素的总数量,m 是桶的数量。但通常对哈希表的实现几乎总是使 n = O(m),也就是说,元素的总数绝不会超过桶的总数,所以 O(n/m) 也变成了常量 O(1)。

cuckoo

Cuckoo hash算法分析——其根本思想和bloom filter一致 增加hash函数来解决碰撞 节省了空间但代价是查找次数增加

 基本思想:

cuckoo hash是一种解决hash冲突的方法,其目的是使用简单的hash 函数来提高hash table的利用率,同时保证O(1)的查询时间

基本思想是使用2个hash函数来处理碰撞,从而每个key都对应到2个位置。

插入操作如下:

1. 对key值hash,生成两个hash key值,hashk1和 hashk2, 如果对应的两个位置上有一个为空,那么直接把key插入即可。

2. 否则,任选一个位置,把key值插入,把已经在那个位置的key值踢出来。

3. 被踢出来的key值,需要重新插入,直到没有key被踢出为止。

 

我们先来看看cuckoo hashing有什么特点,它的哈希函数是成对的(具体的实现可以根据需求设计),每一个元素都是两个,分别映射到两个位置,一个是记录的位置,另一个是 备用位置。这个备用位置是处理碰撞时用的,这就要说到cuckoo这个名词的典故了,中文名叫布谷鸟,这种鸟有一种即狡猾又贪婪的习性,它不肯自己筑巢, 而是把蛋下到别的鸟巢里,而且它的幼鸟又会比别的鸟早出生,布谷幼鸟天生有一种残忍的动作,幼鸟会拼命把未出生的其它鸟蛋挤出窝巢,今后以便独享“养父 母”的食物。借助生物学上这一典故,cuckoo hashing处理碰撞的方法,就是把原来占用位置的这个元素踢走,不过被踢出去的元素还要比鸟蛋幸运,因为它还有一个备用位置可以安置,如果备用位置上 还有人,再把它踢走,如此往复。直到被踢的次数达到一个上限,才确认哈希表已满,并执行rehash操作。如下图所示(图片来源):

cuckoo_preview

 

我们不禁要问发生哈希碰撞之前的空间利用率是多少呢?不幸地告诉你,一维数组的哈希表上跟其它哈希函数没什么区别,也就50%而已。但如果是二维的呢?

一个改进的哈希表如下图所示,每个桶(bucket)有4路槽位(slot)。当哈希函数映射到同一个bucket中,在其它三路slot未被填满 之前,是不会有元素被踢的,这大大缓冲了碰撞的几率。笔者自己的简单实现上测过,采用二维哈希表(4路slot)大约80%的占用率(CMU论文数据据说 达到90%以上,应该是扩大了slot关联数目所致)。

cuckoo hashing

 

摘自:http://coolshell.cn/articles/17225.html

 

在这里插入图片描述
(b)里面显然直接去找c,需要的运算更少,但是无法知道走a需要的迭代更多还是走c需要的迭代更多
©造成死循环了

  • 与标准的cuckoo hash盲目地踢出item不一样,我们通过使用多重备份来预测怎么成功地踢掉。
  • 根据每个item有多少个备份在表里,可以识别出不可能的桶,在查询的时候跳过他们
  • 使用stash 策略避免插入失败后的rehash(二度哈希,当h1导致冲突了,尝试使用h2,所有的hash函数都与h1类似,只有乘法因子不一样)
  • 逻辑和数据结构简单,可以很方便地软件和硬件实现。在速度很慢。带宽受限的off-chip 外部内存上很适用

Cuckoo hashing 变种

d-ary Cuckoo[27] 和 blocked Cuckoo[20],采用d个哈希表或者是每个桶里面放L个item,使得载入率超过90%。
多叉Cuckoo

As other answers have pointed out, it’s true that the simplest cuckoo hashtable requires that the table be half (需要51%的空余来保证插入时一个比较好的性能)empty. However, the concept has been generalized to d-ary cuckoo hashing, in which each key has d possible places to nest, as opposed to 2 places in the simple version.
The acceptable load factor increases quickly as d is increased. For only d=3, you can already use around a 75% full table. The downside is that you need d independent hash functions. I’m a fan of Bob Jenkins’ hash functions for this purpose (see http://burtleburtle.net/bob/c/lookup3.c), which you might find useful in a cuckoo hashing implementation.
from Stackoverflow

后续的改进多是这两种的结合[18][19],同时为了减少增加的hash计算,发展出使用GPU的硬件方法和使用double hashing的软件方法。

  • smartCuckoo使用随机森林来预测是否会产生死循环,但是只能在2个hash函数的情况下运行
  • Necklace尝试在备用记录里维护可用bucket来增大找到踢出最小路径的可能性,但是备用记录消耗大量的空间和visiting cost。
  • MinCounter给每个桶分配了5bit的计数器来跟踪桶的踢出历史,会选择占用不那么热门的桶来减少长期的踢出总数。
    上述算法都比标准的Cuckoo hashing效果要好但是消耗了额外的空间和计算资源
    另一种方法叫做Random-walk,减少碰撞时的插入时间。原本的BFS效率不高,是多项式复杂度。Random-walk不需要额外的数据结构就能实现ploy-logarithmic插入时间(随机选择一个item踢出)。但是提升有限。
    rehashing和stash-based机制都被用来解决插入失败。当表中的item数达到理论上界的时候,一个小小的stash(size s)可以把rehashing的概率从1/n降到 1 / n s + 1 1/n^{s+1} 1/ns+1,因此stash一般是放在on-chip,stash经常被访问的。但是如果有大量插入,还是避不开rehashing。
    另一类工作聚焦于off-chip的big Cuckoo tables。DEHT和EMOMA选择在on-chip上用辅助结构来定位,实现one-access查询。但是损失了一些主表中的replacement flexibility来减少on-chip Bloom filter的false positive error。
    false positive 实际上是假的,但是被预测为正的

最新的高级hash技术将AMAC吸收入Cuckoo hashing来提高查找性能,但是***和本文工作正交***?

universal hashing(全域hash)??

Let’s say you have h(x)=x mod m. Then an adversary can choose keys: 1, 1+m, 1+2m, 1+3m etc. They will all cause collisions. But if the function h is chosen randomly from a universal family, you can’t guarantee such a “bad” set of keys.
在这里插入图片描述
在向哈希表中插入元素时,如果所有的元素全部被哈希到同一个桶中,此时数据的存储实际上就是一个链表,那么平均的查找时间为 Θ(n) 。而实际上,任何一个特定的哈希函数都有可能出现这种最坏情况,唯一有效的改进方法就是随机地选择哈希函数,使之独立于要存储的元素。这种方法称作全域哈希(Universal Hashing)。
全域哈希的基本思想是在执行开始时,从一组哈希函数中,随机地抽取一个作为要使用的哈希函数。就像在快速排序中一样,随机化保证了没有哪一种输入会始终导致最坏情况的发生。同时,随机化也使得即使是对同一个输入,算法在每一次执行时的情况也都不一样。这样就确保了对于任何输入,算法都具有较好的平均运行情况。

hasha,b(key) = ((a*key + b) mod p) mod m

其中,p 为一个足够大的质数,使得每一个可能的关键字 key 都落在 0 到 p - 1 的范围内。m 为哈希表中槽位数。任意 a∈{1,2,3,…,p-1},b∈{0,1,2,…,p-1}。

设h是从哈希函数全域集H中随机选出的函数h,h被用作把任意n个键映射到表T的m个槽中,对给定键值x,E[#collision with x]<n/m | 发生碰撞的期望次数小于n/m,即装载因子α

完美hash

在这里插入图片描述
当键值是static(即固定不变)的时候,我们可以涉及方案使得最差情况下的查询性能也很出色,这就是

完美哈希。实际上,很多地方都会用到静态关键字集合。比如一种语言的保留字集合,一张CD-ROM

里的文件名集合。 而完美哈希可以在最坏情况下以O(1)复杂度查找,性能非常出色的。

完美哈希的思想就是采用两级的框架,每一级上都用全域哈希
具体来说,第一级和带链表的哈希非常的相似,只是第一级发生冲突后后面接

的不是链表,而是一个新的哈希表。后面那个哈希结构,我们可以看到前端存储了一些哈希表的基本

性质:m 哈希表槽数;a,b 全域哈希函数要确定的两个值(一般是随机选然后确定下来的),后面跟着

哈希表。

为了保证不冲突,每个二级哈希表的数量是第一级映射到这个槽中元素个数的平方,这样可以保证整个

哈希表非常的稀疏。下面给出一个定理,能更清楚的看到设置m=n^2的作用

哈希表和完美哈希
算法打基础——HashⅡ: 全域哈希与完美哈希

Design and implementation details

general idea

占用所有的buckets避免了冲突时麻烦的踢出,之前的算法被认为是reactive由于更关注kick-out而不是解决碰撞?
McCuckoo takes a proactive approach by keeping the decision on placement open until a more suitable item later on claims one of the buckets and replace the copy in it.
McCuckoo的一个理念是尽可能久地保持所有items的冗余。迟早会大家都只有一份饿了,这个时候用已有的冲突解决机制random-walk或MinCounter来start relocating items。
计数器来跟踪数目。用bit计数,d很少,logd就更小了。(d=3的时候载入率就高于90%了)计数器的作用如下:

  1. 根据简单的逻辑来决定哪个桶可以拿来用,不需要去访问桶
  2. 在查找的时候排除掉不可能的桶,利用合法的candidate buckets应该有相同的计数值
  3. 标记删除,而不需要真的把item移出去

Design Principles

insertion

总体上让每个item占用更多的bucket。只有当所有的candidate buckets含有一份别的items时,才真的出现碰撞。因此想让表中任意一个item的备份数降到1的速度越慢越好
e.g. 1 1 1意味着三个bucket,每个都被只享有一个bucket的item给占据了
插入的方案:

  • 占领所有空的可占buckets
  • 永远不会动计数值为1的buckets
  • 按照counter值的降序去占领可占bucket,until the overwriting results in more copies for the inserted item than the overwritten one.
    3 2 1
    B C A
    2 2 2
    A C A (同时B占领的桶都改为2),此时如果再去占领C在的桶
    3 3 3
    A A A (C占领的桶的计数值都改为1)

3 2 2
B C A
3 2 3
A C A (此时B占领的桶都改为2) 2:3和3:2对全局没什么影响
定理1. 上述插入准则达到全局最优冗余平衡
证明:省略(不是作者省略,是我)
定理2:表中所有冗余写入不会超过 1 + Σ 3 d 1 / t 1+\Sigma_{3}^{d} 1 / t 1+Σ3d1/t倍的表的大小
证明:省略。当d=3时,冗余数不会超过表大小的5/6。这个定理也告诉我们大多数冗余发生在表建立起来的时

查找

with some false positive error but no false negative???
on-chip counters用于减少对主表的查找。

  • 可以找到non-existing items,原理类似于Bloom Filter。在一次插入过程中,所有的candidate bucket会被填满,要么是这个item,要么是之前被占了,计数器里都不会是0。因此如果由一个candidate bucket计数器是0,那么这个item一定没有插入过,就不用费心到off-chip 主表里面查找了。就算假阳性,去主表里查一下也知道不存在。
  • 减少对存在的items查询时的内存访问。由于同一个item占用的所有buckets会有相同的计数值,因此将candidate buckets按照计数值分组,组内bucket个数是s, bucket counter值是c(和自己一样的一共有几个),如果s<c,那么这个组的bucket都不用查了。此外,for the buckets that share a common matching value, checking just one of them is sufficient to return the right answer?我知道它存在,就一定这样可以吗? 如果有3个buckets的c值都是2,就不能排除掉任何一个。可能是这个item确实有两个备份,这个时候只要查找任意两个就能知道到底在不在了。
    类似于这样的观察可以减少不必要的磁盘访问。在实际中,可以实现0次货1次访问,尤其当表适当地loaded时候。

查找d个哈希函数的McCuckoo hash table的准则是

  • 如果candidate bucket有一个值为0,那么return negative
  • 将非零的candidate buckets按照c值划分,跳过所有的集合大小小于c值的集合
  • 对于剩下的集合,集合大小是S,c值是V,那么检查S-V+1个buckets就能知道在不在。

定理3: 除非所有的c值都是1,否则上述查询策略总能够减小查询范围。
证明:忽略
推断:除非表很满,(很多bucket c值为1),否则总是能减少查询时的消耗

deletion

删除的时候需要把对应的所有candidate bucket给删了,这样子的话要多次访问。为了避免这种情况,把对应的计数器标记为0,但是单纯标记为0不行,举个例子,A映射到bucket 5,插入,B映射到bucket 5,选择不踢出A,此时我删除A,把bucket 5的值给设置为0,那么我查询B的时候,会以为B也不在。这样是不OK的。因此设置一个deleted flag,当这个flag是true的时候,忽略第一条查找规则,这个会在插入的时候视为0,在搜索的时候视为非0。别的item再进来的时候,再把counter设置为正确的数值。
缺点:一个bucket被插入后,c值永不为0,因此随着插入越来越多,过滤不存在的item的能力会越来越差。因此适用于删除很少发生的场景。

删除的时候剩下两条查找规则还能用吗?能。

删除的准则如下:

  • 跳过所有c值为0或标记为deleted的桶
  • 将非零的candidate buckets按照c值划分,跳过所有的集合大小小于c值的集合
  • 对于剩下的集合,集合大小是S,c值是V,检查S-V+1个buckets,如果item在,就一直找,把所有V个bucket都标记为删除,否则返回。

d=3 is actually sufficient for most practical scenarios

Data Structure

在这里插入图片描述

Collision Resolution

当待插入的item发现所有的candidate bucket都是1时,就很绝望,需要踢出一个。采用random-walk方案。
RW随机选择一个bucket踢出item,再把item给插入表中。嗯哼?和普通的有啥区别?
比方说每个item B,在它的备用桶中的分布是2 2 1,它占的是1,那么item A来的时候,发现所有的槽都是1,如果它踢掉B,那么B可以回去overwrite别的桶,计划通!
但是如果B的备用桶中分布是1 1 1,那么B就要再随机踢掉一个~~~
备注:按照插入时占的尽可能均匀,不会出现3 2 1(B) 的情况,B会把第一个bucket也给占的。
这个样子?
和普通的区别在于什么呢?还是要去看一下RW算法。

只是为了说明on-chip 计数器可以帮助更快找到冲突解决方案么。
但是当装载的太多的时候,会发现buckets都是1,没有解决方案了,插入失败。那么会用stash-based approach。

Working with a Stash

为了避免rehashing,把插入失败的都放到stash里面去。stash的主要问题在于主表中找不到我们每次都会去找stash,这样它就被频繁访问,现有的策略都是把它放在on-chip memory,限制了它的大小。而且即使它on-chip,查询它也会消耗CPU时间片。但是呢用别的方法去过滤不必要的查找又会带来额外的空间和计算消耗,因此本文直接查找small stash。

把stash off-chip。
在这里插入图片描述
增加一个1bit的flag,向Bloom Filter那样工作,如果插入失败,就把所有的candidate buckets flag设置为1,如果不是全1,就知道不在stash里面。简言之,找不到可能有这么几种情况:压根没插入,插入后被删除,插入失败,它的高效在于:

  • 计数器已经能过滤掉大多数不存在的items的查找,都不会去查主表,更不用说stash了
  • 如果一个item被放入stash->插入失败->candidate buckets = 1。而且在McCuckoo里面,事已至此,计数器的值也不会再增加了(增加意味着碰撞时应该有解)。所以如果某个candidate buckets值>1,那么一定不在stash里,不需要查询stash。
  • 如果所有的candidate buckets = 1,但在主表里也没有找到,这个时候要去查看flag。在上一步的时候已经读取,不需要额外读。这一步的意义大概在于,candidate buckets = 1,但是可能压根就没被插入过,何必去找stash。或者是被删除了,之后被占的,而不是插入失败进入。
  • 全部都是1还没找到,它可能进入stash后被删除了,也可能别的items进入stash把它全部碰撞了。

Handle Deletion Aftermath

这一段都读的云里雾里的。总的来说是解决删除操作带了的问题

  1. 为了贯彻McCuckoo的思想,要尝试在删除的时候尽快地填充empty buckets来增大冗余。但是找一个合适的item去占领释放了的bucket很难,因为我们不知道counter的信息? 因此只在下一次插入的时候,快速占领这个空桶。这会在删除频率远小于插入频率的时候降低一点桶的利用率。

我们应该思考,Bloom filter是不支持删除的,所以当删除的时候

  1. 当the bucket重新被占领的时候,计数器可能是任意值。因此不能根据计数器的值来排除item被存储在stash中的可能性了。见上方计数器高效点2。
    ?那上面那条还说啥? 直接排除删除的情况吗?
    但是仍然可以检查flags(忽略skipped buckets),这些flags在查询的时候已经从桶里 取出来了,如果有flag值为0就不去检查stash。由于决策由更少的flags决定,因此假正率会增高,但是不会有假错率。意思是我绝对不会去额外地读取candidate bucket的flag值,我只会先在主表里按照查询的规则查询bucket,查的过程中顺便把flag给揪出来。这就导致了实际用的flag数是小于d的。由于stash中item的数量比主表小很多,所以大多数的flag都是0,即使在查找的时候只依靠少量的flags,false positive率会搞一点,但是还是能过滤掉大多数不必要的对stash的访问。

  2. 如果删除了stash里的,Bloom filter是不支持删除的。在McCuckoo中选择不更新flag,因此false positive会随着stash item删除数量的增多而增大。但是由于stash items比起主表里的要少,false positive是可接受的,对预审的影响也是有限的。在一系列删除操作后,我们可以选择把flags重置为0,重新把stash items插入到主表中,这样子的话flags的状态就会与新插入stash的item同步。
    4.在这里插入图片描述

Extension on multi-slot Cuckoo structure

在这里插入图片描述
blocked version of Cuckoo hashing:一个桶分为l个slots,可以将表的负载提升到很接近100%。
右边每张表的大小是原来的1/3?To accommodate the same number of n items, now the length of each table is reduced to roughly one third of the size as before, which is m/3. ?
现在是每个slot有一个counter。
现在的问题是item placement不能简单地由counters跟踪了
比如,之前我们要更新item占用的桶,只要知道了桶就很容易知道计数器。但是现在知道这个copy在哪个桶里不足以定位计数器,因为可能在任意一个slot里,现在的on-chip结构是没法跟踪到的。为了正确性,对于每份copy,需要去把它从bucket里读出来看它到底在哪个slot里。简单地说,就是当不管插入、查询还是什么,需要知道candidate bucket的c值时,我得把另外bucket给遍历一遍看它在哪个slot,才能获得C值。
一种减少off-chip访问的方式如图5:在主表里同时记录另外的copy对应的slot号。因此当我们想要更新一个item的copies时,我们只需要从主表中读取。对一个d-hash l-slot的McCuckoo,额外的内存开销是每个slot(d-1)log(l) bits(要存d-1个位置,位置可能从1~l,因此用log(l)bit来存)
把多-slot迁移过来会面临很多问题,为了效率只处理简单的,不处理复杂的😂
只提了multi-slot extension的三个重要的改变

  1. 将bucket视作一个整体来做决策,该bucket所有slot的c值的和作为bucket的availability,***越高越好?***只有当9个counters都是1,才视作一次collision,因此多插槽的McCuckoo的载入率可以很高而不需要考虑处理碰撞。
  2. 但是查找的时候由于一个bucket里有很多item,the combination of their status is difficult to trace with just the counters,不能仅在on-chip就排除一整个bucket,因此查询更像是传统的那样不怎么依赖于计数器。
  3. stash flags仍然是映射到each bucket,而不是slot,因为考虑到stash里面的items数目少,在bucket级别预筛会更简单更快。
    在这里插入图片描述
    在这里插入图片描述

Concurrency and multiset

标准的Cuckoo hash是线性的,不支持对表并发读写。因为在踢出的过程中,不仅接下来踢的过程依赖于前一个,而且被选中(暂时,还在往下迭代,这条路不一定走得通)被踢的item会暂时性的不在表中。由于查询操作比插入和删除要多很多,因此采用one-writer-many-reader concurrency足够应对与read-heavy的人物了。MemC3引入了cuckoo path的概念,提出了两种对插入顺序的简单修改来实现基于cuckoo path的one-writer-many-reader concurrency,但是没有高效的方法找到这么一条路。McCuckoo擅长快速找到short cuckoo path,因此结合两者能给McCuckoo带来高效的并发支持。

??
McCuckoo can also support multiset, but not by distributing items of the same key among that keys multiple copies, because those redundant copies should always be identical; instead it can act as an indexing structure pointing to the address where all those items are actually stored.

Insertion
先在空的里面占着,如果最后size(S)不大于1,就返回 size=1就没必要再继续下去了,已经占大于一半了?

按照总counters排序(最大是9),如果找到一个槽的counter值=3,占领,
这个插入算法我怎么有点看不透?

Experiment Evaluation

实现了d-ary和blocked form McCuckoo,并以stash作为插入失败的处理方式。(d-ary就是d个hash函数,blocked form是一个bucket有多个slot)。同时用标准的d-ary Cuckoo和blocked Cuckoo Hash Table BCHT作为基线对比。
由于McCuckoo的目的是尽最大可能减少对off-chip memory的访问,先测试这个方面的性能。

Exparimental Environments

hardware platform

All the simulations were carried out on a machine with 4-cores (8 threads, Intel Core i7@2.60 GHz) and with 8 GB of DRAM memory. The target platform is an Altera Stratix V GX FPGA from Intel with 4.5MB on-chip SRAM with support to external DDR3 SDRAM memory at 800Mhz. In our FPGA implementation, the logic runs at 333Mhz and the DDR3 memory controller runs at 200Mhz, respectively.

DataSet and Implementation

DocWords:This dataset includes five text collections in the form of bag-of-words and we choose the one collected from NYTimes news articles. It contains approximately 70 million items in total. The DocID and WordID are combined to form the key of each item and inserted into the hash tables.
BoW(词袋)模型详细介绍
Bag-of-words model (BoW model) 最早出现在自然语言处理(Natural Language Processing)和信息检索(Information Retrieval)领域.。该模型忽略掉文本的语法和语序等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的。BoW使用一组无序的单词(words)来表达一段文字或一个文档.。
基于文本的BoW模型的一个简单例子如下:

首先给出两个简单的文本文档如下:

    John likes to watch movies. Mary likes too.

    John also likes to watch football games.

基于上述两个文档中出现的单词,构建如下一个词典 (dictionary):

   {"John": 1, "likes": 2,"to": 3, "watch": 4, "movies": 5,"also": 6, "football": 7, "games": 8,"Mary": 9, "too": 10}

上面的词典中包含10个单词, 每个单词有唯一的索引, 那么每个文本我们可以使用一个10维的向量来表示。如下:

   [1, 2, 1, 1, 1, 0, 0, 0, 1, 1]

   [1, 1,1, 1, 0, 1, 1, 1, 0, 0]

该向量与原来文本中单词出现的顺序没有关系,而是词典中每个单词在文本中出现的频率。
代码用C++写的,hash function选的是BOB hash。
对于FPGA平台,由于on-chip SRAM空间有限,我们提取了6百万子集来测试,hash 函数没有用BOB hash而是涉及了模和位运算。

experimental settings

d=3, l=3
每个实验做10次取性能平均值。

insertion performance

比较了不同载入率下每次插入的踢出数,以及涉及到的对内存的读写。(快速找到可插位置和相关cost)。
同时记录了第一次kick-out和第一次插入失败时候的载入率,因为when a hash table is filling up with items, increased availability can defer the occurrence of collisions and insertion failure

在这里插入图片描述
在载入率很低的情况下几乎所有的插入都不会kick-out。multi-copy schemes可以帮助处理碰撞,减少踢出的次数
在这里插入图片描述
由于McCuckoo需要额外的copies可能会在插入时有额外的内存存取,同时测量了不同载入率下的平均读写次数。
在这里插入图片描述
figure 9 a 为什么没有往后画,是不是性能崩了
读可以降低到0,因为可以在on-chip中就知道哪些bucket是空的,Furthermore, the multiplier effect is more severe for the single-copy schemes if we compare Fig.10a with Fig. 9, because they need to read back each candidate bucket to know
if they are empty or not during the kick-outs, while the multi- copy schemes can figure out the empty buckets with the on- chip counters.

关于写,McCuckoo方案在低载入率的时候要比single copy的方案要高。在高载入率的时候,writes caused by kick-out会增加。对于单copy的,转折点发生在50%,对于McCuckoo,要更高一点点。
. The cross-over happens at about half load for single-slot schemes and at a bit higher load for the multi-slot schemes, which means for the most likely working conditions of Cuckoo with the table moderately to heavily loaded, the number of writes is also lower with the multi-copy schemes. Since more reads will take place than writes during an insertion, the total number of accesses at higher load ratio is much reduced in the multi-copy schemes.
McCuckoo可以更久地保持collision-free的状态在这里插入图片描述
第一次插入失败是很关键的,因为从这之后,more insertions will stop at maxloop which costs heavily for us。

将maxloop从50改到500,可以发现,越高的maxloop可以获得越高的负债率,但是插入失败同时也带了更大的罚时but also induce heavier penalization if an insertion still fails after the lengthy trial.

From the figure we can see that with multi-copy we can reach higher load ratio free of insertion failures with the same maxloop, or reach the same load ratio with smaller maxloop values than the single-copy schemes.

Lookups for Existing and No-existing Items

在这里插入图片描述
B-Cuckoo负载多了就要像普通的方式那样去查
可见插入性能优越
在这里插入图片描述

Deletion

删除性能不太好,但是删除操作少,影响不大

在这里插入图片描述

Stash at High Load Ratio

由于第一次踢出把stash放在off-chip,因此么得对比
show the necessity of a bigger stash and the feasibility of putting one off-chip
模拟a McCuckoo table and a blocked McCuckoo table在非常接近于最大负载,主表非常拥挤,每个表有五个参数,当前负债率,maxloop,stash中的item数,占插入总数的比例,到stash中查询不存在的item的比例。
从实验结果中可以看到,大的stash是很有必要的。将item插入到拥挤的表格中非常有可能落入到stash中,除非可以接受一次rehash。预筛机制也很好地避免了大多数不必要查询。
在这里插入图片描述

Latency and Throughput

硬件上,和软件好像有点不一样
The evaluation on latency and throughput is based on a Altera FPGA development board that can run the McCuckoo logic and access the on-chip SRAM at 333Mhz. Hash calculation and the logic is implemented in hardware that can be performed in 1CLK. The on-chip SRAM can be read in 3CLK and written in 1CLK. For the off-chip DDR3 SDRAM, the controller is clocked at 200Mhz, and read costs about 18CLK on average and write costs 1CLK.
读时延要大于写时延,写完就可以返回执行下一个指令,但是读还要等数据从外部内存回来。同时,当记录的size小的时候,skip checking some buckets在读时延上没有明显的区别,因此McCuckoo的好处就不是那么显著(while the time to access the on-chip counters becomes relative large because they need to be checked all the time)

硬件上对存在item和不存在item的查找时延和throughput
show lookup latency in (a)&(b) and throughput in ©&(d) for existing items to the left and non-existing ones to the right.
因此建议跳过counters直接查,不影响结果。

大揭秘

随机游走
The key question is which item to move if the d potential
locations for a newly inserted item x are occupied. A natural approach in practice is to pick one
of the d locations randomly, replace the item y at that location with x, and then try to place y in
one of its other d − 1 location choices [6]. If all of the locations for y are full, choose one of the
other d−1 locations (other than the one that now contains x, to avoid the obvious cycle) randomly,
replace the item there with y, and continue in the same fashion. At each step (after the first), place
the item if possible, and if not randomly exchange the item with one of d − 1 choices. We refer to
this as the random-walk insertion method for cuckoo hashing.
就是随机选一个踢掉啦,起了那么一个高大上的名字orz
An Analysis of Random-Walk Cuckoo Hashing

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值