fast hash table-学习笔记

引入知识:hash table
hash table原理
引入知识:布隆过滤器
Bloom filters
布隆过滤器代码

想要学习一下fast hash table,没有找到中文资料,只能生啃论文了。
本篇博客主要是翻译fast hash table的一篇优秀论文:Song H, Dharmapurikar S, Turner J, et al. Fast hash table lookup using extended bloom filter: an aid to network processing[J]. ACM SIGCOMM Computer Communication Review, 2005, 35(4): 181-192.

摘要

哈希表是几种网络处理算法和应用程序的基本组成部分,包括路由查找,数据包分类,每流(per-flow)状态管理和网络监视。这些应用程序通常出现在高速路由器的数据路径中,它们必须用很少的缓冲或者不用缓冲来处理或转发数据包,因此保持整个线速度非常重要。
设计不良的哈希表可能会严重影响应用程序的最坏情况吞吐量,因为每次查找所需的内存访问次数可能会有所不同。因此,高吞吐量应用程序需要具有可预测的最坏情况查找性能的哈希表。然而已发表的论文通常假设哈希表查找花费的时间是恒定的,但是在典型的哈希表搜索中必须访问的项数存在很大差异,导致搜索时间相差四倍或更多。
我们提出了一种新颖的哈希表数据结构和查找算法,该算法通过减少最耗时的查找所需的内存访问次数来提高朴素哈希表的性能。这使设计人员可以在给定的带宽下实现更高的查找性能,而无需在查找引擎之前进行大量缓冲。我们的算法扩展了多重哈希的Bloom Filter数据结构,以支持精确匹配,并利用嵌入式内存技术的最新进展。通过分析和模拟的组合,我们表明我们的算法比使用相同内存量的朴素哈希表要快得多,因此它可以为使用哈希表的路由器应用程序提供更好的吞吐量。

简介

哈希表是一种通用数据结构,用于执行快速关联查找,这需要每次查找O(1)次平均内存访问。 实际上,由于其在网络数据包处理中的广泛适用性,一些现代网络处理器提供了内置的哈希单元。 一份有关网络数据包处理的最新研究文献的调查显示,哈希表对于许多应用程序都是通用的,包括按流状态管理,IP查找和数据包分类。 这些应用程序通常出现在高速路由器的数据路径中。 因此,它们必须能够以线速处理数据包,这使得基础散列表必须提供良好的查找性能。

fast hash table算法

为了清楚起见,我们从朴素的哈希表(NHT)开始逐步开发算法和哈希表体系结构。
我们采用哈希表算法中的链地址法解决冲突,因为它比开放式寻址方案具有更好的性能,并且是最流行的方法之一。
现在,我们介​​绍我们的快速哈希表(FHT)算法。

基本快速哈希表(BFHT)

首先介绍快速哈希表的基本形式,我们称其为基本快速哈希表(BFHT),然后对其进行改进。快速哈希表基于布隆过滤器的一种变种,称为计数布隆过滤器,其中过滤器的每一位都被一个计数器代替。在插入项目后,由相应哈希值索引的每个计数器都会增加。由于其结构,此过滤器中的计数器实质上为我们提供了其中散列的项目数。我们将在表中显示如何有效地使用此信息以最小化搜索时间。
我们维护一个由m个计数器组成的数组C,其中每个计数器Ci与哈希表的存储区(桶)i相关联。我们在输入项上计算哈希函数h1(),h2()…,hk()并递增由这些哈希值索引的相应K个计数器。然后,我们将该项目存储在与每个存储桶相关的列表中。
因此,单个项目被多次存储在片外存储器中。下面是插入一个项目的算法描述。
在这里插入图片描述
请注意,如果有多个散列函数映射到同一地址,则我们只会将计数器递增一次,并且只会在该存储桶中存储一项的副本。 为了检查哈希值是否冲突,我们将先前为该项目计算的所有哈希值保留在寄存器中,并将新哈希值与所有哈希值进行比较(上述插入算法第2行)。
如下图所示,四个不同的项目x,y,z和w已依次插入。 每个项目都复制到k = 3个不同的存储桶中,并且与该存储桶关联的计数器值反映了其中的项目数。
图2搜索过程类似于插入过程:给定要搜索的项x,我们计算k个散列值并读取相应的计数器。当所有计数器都不为零时,过滤器将指示表中存在输入项。仅在此步骤之后,我们才通过将其与一个存储桶相关联的项目列表中的每个项目进行比较,来在片外表中进行验证。事实上,如果将计数器保存在快速片上存储器(on-chip)中,这样就可以并行检查与该项目关联的所有随机计数器,然后在几乎所有情况下,如果表中不包含给定的项目,我们就避免了片外访问。考虑到嵌入式存储器技术的最新进展,可以想象在高速多端口片上存储器中实现这些计数器。
其次,要检查列表的选择至关重要,因为列表遍历时间取决于列表长度。因此,我们选择与计数器关联的具有最小值的列表,以减少片外存储器访问。我们算法的加速来自以下事实:它可以选择最小的列表进行搜索,因为NHT只能跟踪可能包含多个项目的一个列表。
在大多数情况下,对于经过仔细选择的存储桶数,最小值计数器的值为1,仅需要单个存储器即可访问片外存储器。在图2所示的示例中,如果查询项目y,则仅需要访问列表X11,而不是长度比X11长的X3或X6。
当由输入项目索引的多个计数器具有相同的最小值时,则必须以某种方式打破平局。我们通过简单地选择索引最小的最小值计数器来打破这种关系。例如,在图2中,项目x的两个存储桶计数器设置为2,这也是最小值。在这种情况下,我们总是访问存储桶X1。
以下伪代码总结了BFHT上的搜索算法。
在这里插入图片描述

最后,如果输入的项目不存在于项目列表中,则很明显这是计数布隆过滤器指示的误报匹配。
使用上面的数据结构,删除项目很容易。 我们只需递减与该项目关联的计数器,然后从相应列表中删除所有副本。
在这里插入图片描述

修剪的快速哈希表(PFHT)

在BFHT中,我们需要维护每个项目的k个副本,这需要比NHT多k倍的内存。 但是,可以观察到,在BFHT中,探查表时只能访问每个项目的一个副本(与最小计数器值关联的副本)。 永远不会访问该项目的其余(k-1)个副本。 此观察结果为我们提供了进行内存优化的第一个机会:现在可以删除该项目的所有其他副本,但在搜索过程中访问的副本除外。
因此,在此修剪过程之后,我们只有一个副本,该副本将内存需求减少到与NHT相同。我们将生成的哈希表称为修剪的快速哈希表(PFHT)。
以下伪代码总结了修剪算法。
在这里插入图片描述
修剪过程如图3(A)所示。要注意,在修剪过程中,计数器值不会更改。因此,修剪完成后,计数器值不再反映列表中实际显示的项数,通常大于该值。但是,对于给定的项目,计数器值最小的存储桶始终包含该项目。此属性可确保搜索结果的正确性。修剪的另一个特性是,它与修剪项目的顺序无关,因为它仅取决于不会更改的计数器值。因此,按顺序在x-y-z-w中进行修剪将产生与在z-y-x-w中进行修剪相同的结果。
修剪过程的局限性在于,现在很难执行对表的增量更新。由于计数器值不再反映列表中的项目数,因此如果分别为任何新插入或删除操作增加或减少计数器,则可能会干扰与存储桶中现有项目相对应的计数器值,从而导致错误的结果搜索。例如,在图3(A)中,项y分别映射到具有计数器值{3,2,1}的列表{X3,X6,X11}。

在这里插入图片描述
如果插入了也恰好共享存储桶11的新项目(例如v),则计数器将增加到2。因此,具有与y关联的最小索引的最小计数器值存储桶不再是11而是6,但是6根本不包含y。因此,对y进行搜索将导致错误的结果。受此限制,对于任何新的插入和删除,必须从头开始重新构建表,这对于可变项集来说可能会使该算法不切实际。
现在我们描述一个可以增量执行的InsertItem和DeleteItem算法的版本。这些功能中使用的基本思想是保持按项目索引的k个存储桶不变,应始终将其放置在计数器值最小的存储桶中。如果是平局,应将其放在索引最小的那一列。如果在每个点都保持不变性,无论插入项目的顺序如何,生成的哈希表配置将始终相同。
为了插入一个项目,我们首先增加相应的k个计数器。如果这些存储桶中已经有任何物品,则可能会更改其相应的最小计数器。但是,计数器的增量不会影响所有其他项目。因此,必须将每个项目重新插入表中。换句话说,要插入一个项目,我们需要重新考虑所有k个存储桶中的所有项目。
以下伪代码描述了插入算法。
在这里插入图片描述
在上面的伪代码中,Y表示要考虑插入的项目列表。首先将其初始化为x,因为这是我们要插入的项目(第1行)。然后对于x映射到的每个存储桶,如果尚未考虑存储桶(第3行),则我们增加计数器(第6行),收集与之关联的项目列表(第4行),因为现在必须重新考虑所有这些项目,并从存储桶中删除所有项目(第5行)。最后,重新插入所有已关联的项目(第8-10行)。要注意,由于项目早已插入,因此在重新插入计数器时无需增加计数器。在这里只是更改它们所在的存储桶。
由于数据结构在m个存储桶中存储了n个项目,每个存储桶的平均项目数为n / m。因此,从存储桶读取的项目总数为nk / m,需要进行多次内存访问。最后,将1 + nk / m项插入到表中,这又需要进行很多的内存访问。因此,插入过程的总复杂度为O(1 + 2nk / m)次。此外,对于最佳的布隆滤波器配置,k = mln 2 / n。因此,插入所需的总内存访问为1 + 2ln = 2.44。
不幸的是,增量删除不像插入那么简单。删除项目时,我们需要减少相应的计数器。这可能会使这些计数器变成散列到它们的某些项目的最小计数器。但是,由于我们现在仅保留每个项目的一个副本,因此,如果该项目不在该存储桶中,我们就无法确定哪些项目哈希到给定的存储桶。这可以借助仅预修剪的数据结构(即BFHT)来说明,将项目插入所有k个存储桶中,我们知道哪些项目散列到给定存储桶中。因此,为了执行增量删除,我们必须维护如图2所示的离线BFHT。可以在负责更新表的路由器软件中维护这种数据结构。
为了区分离线BFHT和在线PFHT,我们用X表示离线列表,用S表示对应的计数器。则,Xi表示与存储区i相关联的项目列表,Xij表示与Xi有关的第j个项目,而Si表示对应的计数器。
以下伪代码描述了删除算法。

在这里插入图片描述
当我们要删除项目时,我们首先使用DeleteItem BFHT算法(第2-5行)对离线数据结构执行删除操作。然后,我们将所有受影响的BFHT存储桶(计数器递减的存储桶)中的所有项目收集起来以重新插入。同时,我们从PFHT中删除了与每个存储桶关联的项目列表,因为现在必须重新插入每个项目(第7-8行)。最后,对于收集到的项目列表中的每个项目,我们都像在InsertItem中一样重新插入(第9-12行)PFHT。注意DeleteItem的第6-12行与InsertItem PFHT的第4-10行之间的相似之处。唯一的区别是,在DeleteItem PFHT中,我们从BFHT收集要重新插入的项目并且递减计数器而不是递增计数器。
在我们得出DeleteItem算法的复杂度的表达式之前,我们注意到涉及两种类型的操作:BFHT和PFHT。由于BFHT操作可以在后台执行而不会妨碍PFHT的正常操作,因此我们仅得出PFHT操作的复杂性。考虑到这一点,我们注意到BFHT中每个非空桶的项目数为2nk / m,因为在最佳配置中只有一半的桶是非空的(请参阅第3节)。由于我们从k个桶中收集物品,因此我们总共需要重新调整2nk ^ 2 / m个项目(在第9行的循环中)。为了重新调整,我们需要读取和写入每个项目。因此,删除操作的总体复杂度为O(4nk ^ 2 / m)。通过对表进行最佳配置,它可以简化为4kln2 = 2.8k。

优化

(未完待续…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值