千万别!

非常多人这样说,也包括我。

Linux内核早就把HASH路由表去掉了。如今就仅仅剩下TRIE了,只是我还是希望就这两种数据结构展开一些形而上的讨论。

1.hash和trie/radix

hash和tire事实上是能够统一在一起的。具有同样hash值的多个项具有一个共同的特征,这个特征怎么提取呢?无疑这就是hash函数的工作。而trie树(或者radix树,管它呢)的一棵子树也有共同的特征,这个特征怎么提取呢?无疑这就是该子树根节点的父节点指示的某些bits在这棵子树的每个节点都具有同样的值。

实际上,trie树就是hash的一种特殊形式,其hash函数为:取某些bits

trie_hash(value, level)
{
return value & level.bits;
}

那么。这么看来,子树的全部节点都应处在一个“冲突链表”里面了...trie树的做法就是“再次hash”,hash函数随之改变。变成取level.bits更低的某些bits了。如此看来。hash路由表解决海量路由项情况下冲突链表变长的方案就是再次hash了,hash函数变成什么呢?我们后面再谈。

2.TCAM的hash

TCAM在非常多地方被用到,它用来依据内容查索引,常被用于路由查询。CPU Cache查询等,以CPU Cache为例。输入TCAM的内容就是一个内存地址,而输出的结果是一个索引。cache匹配的过程就是取到索引指示的cache line,然后比較输入内容(地址)和该cache line指示的地址是否一致。一致就是命中。
       那么TCAM中最核心的过程就是依据地址得到索引的过程,一般的做法就是hash,由于硬连线实现,hash函数绝对不能有太多的计算。因此一般的做法就是“取地址某些bits”,比方取4到7位一共4位,将一个32位(32位系统,物理地址索引cache为例为例)的慢速物理内存地址映射到4位高速cache索引。形成一个金字塔存储结构。32位到4位的映射,丢失了的28位会形成非常大可能性的冲突,而这个就是时间局部性和空间局部性来尽力弥补了,了解列维飞行的应该知道局部性的伟大含义,它构建了我们整个人类文明。
       最简单的hash函数就是取模,实际上也是“取某些bits”,它更加特殊,它是“取最低N bits"。

3.hash和trie树的统一

trie树实际上是从高位到低位逐步hash的过程构建的,其hash函数就是”取某些bits“。

4.查字典的样例-查英文和查汉字

我们小学的时候查字典一般分为音序查法和部首查法,它就形象能体现hash和trie的不同。

为了简便,我以英文单词查法和汉字部首查法为例。
       英文单词是严格一维度顺序排列的,且仅有26个字母组成。因此它能够依照trie树的方式查询,比方what。who,where,前两个字符都是wh,因此说它们具有这么一个共同特征。假设将取这个共同特征作为hash函数,那么在aaa。cc,sahidad。fwfwew,what,qwert,azsx,who。eee,ooo,where中查询who,what。who,where将形成冲突链表,可是一步运算大大降低了匹配的数量,从11个减为3个,然后再进一步hash,依照字母顺序可知at,wre。o这个顺序,直接取第三个孩子节点。因此英语词典的查询方式非常简便。就是一个不断hash定位的过程。hash函数就是”取某些连续字符“。
       我们再看看汉字部首查询法。它是一个典型的计算型hash函数的不断hash的过程。比方在杨。林,棵,马,牛,猪,过。皮这几个字中查”林“字,由于汉字不是一维结构而是二维结构。它的构成是笔画。不是排序的,因此”取某些字符“的方式全然失效(从哪个方向開始取?...怎么算一个字符?...),因此就须要又一次构造hash函数了。长期的历史形成的汉子具有某种象形的意义。通过观察。我们发现”木“字旁是一个特征,这个计算过程,也就是hash函数运行过程是我们的大脑来完毕的。假设说”取某些字符“更加适用于硬件实现,那么发现偏旁部首则更加适合软件实现,从中我们也能够分析出中国人和西方人的思维之差别。继续往下说。发现”木字旁“之后。杨。林,棵形成了冲突链表,但大大降低了匹配候选字的数量。不想遍历的话。须要再次hash,新华字典设计了笔画数这个再hash函数,”林“字除了偏旁之外还剩下4笔画。于是定位到了”林“。假设还冲突,那就须要遍历了,由于商务印书馆可能想不出什么hash函数了(我不知道这样的汉字部首查字法是谁发明的,就当是出版社的杰作吧...)。

反过来看英文查法。总是能够终于确定性定位,由于它的不断hash的hash函数是”取连续字符“,加之单词长度有限且一维排列顺序递进。总是能够到最后一个字符的。

看出差别了吗?看出trie树查询和hash查询的差别了吗?

5.hash路由表和trie路由表

对于hash路由表查询而言,最长前缀匹配逻辑并没有包括在hash过程中,它来自于一种冒险行为,前提是对hash函数的足够自信。hash路由表查找直接从32位前缀hash表開始。逐步回归到0位前缀hash表,期望在这个过程中能高速得到第一个结果,这第一个匹配结果就是终于结果。
       对于trie路由表查询而言,最长前缀匹配逻辑包括在不断再hash的逻辑中。它匹配的是最后一个结果而不是第一个,由于”顺序取某些bits“不断hash的过程。最后匹配到的显然是最精确的。这是和hash路由查询的本质差别。trie查询没有冒险行为,它不须要冒遍历超长冲突链表之险,由于老老实实地运行顺序取bits这个过程总能将查询过程引到目的地。

6.海量路由项的情况

Linux之所以用了那么久hash路由表组织,是由于它足够了。

由于在大部分时间。路由表项数量是不多的。即便是遍历也不会有太大的开销,而hash的计算会大大降低遍历的开销,所谓的冒险最坏情况就是遍历整个路由项,这不是为题。可是一旦遍历整个路由表的全部路由项真的成了一个大风险的时候。或者说即使遍历一半也吃不消的时候,用hash就不明智了。

这和狮子追羚羊时的博弈相似。一个风险是一顿饭,一个风险是一条命,这是严格不正确称的。所以总是看到羚羊胜利(还真不能把这个当零和游戏,由于狮子有时真的不在乎)。
       如今的问题是,怎样使用hash路由表并降低风险。我们先看一下Linux自己的hash函数:

static inline u32 fn_hash(__be32 key, struct fn_zone *fz)
{
u32 h = ntohl(key)>>(32 - fz->fz_order);
h ^= (h>>20);
h ^= (h>>10);
h ^= (h>>5);
h &= FZ_HASHMASK(fz);
return h;
}

可见它将输入的非0项散列得足够开,可是hash的本质就是大空间往小空间映射。冲突在所难免。

有人提出(比方我)在海量路由表项时将长冲突链表组织成trie树的形式,可是这有意义吗?假设是一个完整的trie路由表。最长32步(考虑压缩和回溯)就能找到结果。假设採用hash+trie的方式,每一步的最坏结果都是32步,一共进行32步...这样做没有意义。
       海量路由表项时,hash小空间是严格有范围的。能够觉得它是固定的。平均情况非常easy通过地址空间和hash空间求得,最坏情况则是全然遍历。平均情况假设都不能接受,难道值得为最好情况去冒险吗?因此,千万别用hash表存储海量路由表项。
       可是。还没完

7.局部性利用以及DoS

32位系统,CPU Cache相比内存而言非常小,怎么能够带来如此大的优化?全部映射到同一个cache line的地址都是冲突的啊...这是由于CPU Cache利用了程序的时间/空间局部性,而对于路由而言。则没有空间局部性。时间局部性能够用于路由cache,然而用于路由表本身则有难度。路由表和CPU Cache的差别在于它是全然的。不存在被替换和老化的问题。因此能够把好的hash函数用于单独的路由cache,而路由表仅仅用于路由cache不命中的情况下去匹配。

理想情况分析完了,剩下的仅仅是悲哀了。
       网络訪问的时间局部性真的能够利用吗?尽管一个5元组的数据流通常会随着时间持续经过路由器,可是假设hash冲突的还有一个数据流也经过的话,就会造成cache抖动,在CPU Cache看来。这个问题能够通过控制task切换或者添加cache line唯一键值来解决,可是对于网络訪问,你没法阻止不论什么一个数据包的到来,仅仅要到来就要查询路由表,就有可能导致cache抖动。更严重的。路由cache非常easy受到精心构造的数据包的攻击造成不可用,频繁的替换或者无限的加长链表。平添了查询开销。
       因此设计一个全然的转发表而不是利用路由cache更加能提升转发效率。

这又一次为我的DxR Pro结构作了一个广告。

海量路由表能够使用HASH表存储吗-HASH查找和TRIE树查找的更多相关文章

  1. NGINX(三)HASH表

    前言 nginx的hash表有几种不同的种类, 不过都是以ngx_hash_t为基础的, ngx_hash_t是最普通的hash表, 冲突采用的是链地址法, 不过这里冲突的元素不是一个链表, 而是一个 ...

  2. Hash表

    Hash表 Hash表也称散列表,也有直接译作哈希表,Hash表是一种特殊的数据结构,它同数组.链表以及二叉排序树等相比较有很明显的区别,它能够快速定位到想要查找的记录,而不是与表中存在的记录的关键字 ...

  3. java数据结构之hash表

    转自:http://www.cnblogs.com/dolphin0520/archive/2012/09/28/2700000.html Hash表也称散列表,也有直接译作哈希表,Hash表是一种特 ...

  4. 索引,B+ tree,动态hash表

    数据库课索引部分的学习笔记. 教材: Database System: The Complete Book, Chapter 15 Database System Implementation, Ch ...

  5. 经典递归问题:0,1背包问题 kmp 用遗传算法来解背包问题,hash表,位图法搜索,最长公共子序列

    0,1背包问题:我写笔记风格就是想到哪里写哪里,有很多是旧的也没删除,代码内部可能有很多重复的东西,但是保证能运行出最后效果 '''学点高大上的遗传算法''' '''首先是Np问题的定义: npc:多 ...

  6. 哈希表(散列表)—Hash表解决地址冲突 C语言实现

    哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.具体的介绍网上有很详 ...

  7. Hash表及hash算法的分析

    Hash表中的一些原理/概念,及根据这些原理/概念: 一.       Hash表概念 二.       Hash构造函数的方法,及适用范围 三.       Hash处理冲突方法,各自特征 四.   ...

  8. Anagrams(hash表)

    Given an array of strings, return all groups of strings that are anagrams. Note: All inputs will be ...

  9. 用链表和数组实现HASH表,几种碰撞冲突解决方法

    Hash算法中要解决一个碰撞冲突的办法,后文中描述了几种解决方法.下面代码中用的是链式地址法,就是用链表和数组实现HASH表. he/*hash table max size*/ #define HA ...

随机推荐

  1. 1042. Shuffling Machine (20) - sstream实现数字转字符串

    题目例如以下: Shuffling is a procedure used to randomize a deck of playing cards. Because standard shuffli ...

  2. 单片机脚本语言-移植lua到stm32-MDK

    Lua简单介绍 Lua[1]  是一个小巧的脚本语言.作者是巴西人.该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. Lua脚本能够非常easy的被C/C++ 代码调用, ...

  3. Android自定义控件(二)——有弹性的ScrollView

    本文在http://gundumw100.iteye.com/blog/1075286的基础上稍作修改, 实现了当手指滑动到ScrollView的顶部.底部时, 可以继续的向上.向下拉伸.当释放手指的 ...

  4. QtWebkit2.2.0 HTML5.0支持情况

      Canvas: 支持element, 2d context以及文本 解析规则:支持 HTML5 tokenizer/tree building,  SVG in text/html, MathML ...

  5. 外观模式-facade实现interface的方式(简单工厂+facade组合使用)

    Façade 外观模式 1.Façade实现为interface的具体过程 在Façade.java 接口 工厂 将构造方法私有  static 方法产生一个工厂 此时 客户端不知道 Façade的存 ...

  6. 练习--分治法--Merge k Sorted Lists

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. / ...

  7. QTableWidget简单操作

    使用Qt设计师工具,在窗体上添加Table Widget控件,这样就可以使用ui全局变量来调用该控件了. Table Widget控件的应用 (1)设置列数和行数 //设¦¨¨置?列¢D数ºy和¨ª行 ...

  8. LeetCode-Divdend two Integers

    题目: Divide two integers without using multiplication, division and mod operator. 思路分析 二分法.将除数不断增倍,而结 ...

  9. NET站点Web部署

    NET站点Web部署(一键发布的实现) 在开发过程中经常需要发布到开发环境.测试环境或者预发布环境上给其他同事进行测试验证效果等等,每次发布都要备份,拷贝,修改配置文件等等重复操作非常的麻烦,效率大打 ...

  10. Jasper_sheetName_defined by parameter or hard coding or filed name

    1.根据传递的参数定义sheet name (jasper sheet name defined by parameter) (1) 获取后台参数 <parameter name="P ...