第三章实现原则.docx
《第三章实现原则.docx》由会员分享,可在线阅读,更多相关《第三章实现原则.docx(11页珍藏版)》请在冰豆网上搜索。
![第三章实现原则.docx](https://file1.bdocx.com/fileroot1/2022-12/6/f1597d20-7660-4960-9d88-0e0f34163319/f1597d20-7660-4960-9d88-0e0f341633191.gif)
第三章实现原则
标题页
本章介绍作者归纳的15条实现原则。
这些实现原则是从比较成功的协议实现中归纳出来的。
其实许多实现者已经有意无意地使用了这些原则,本章只是更清楚地将它们表达出来,以使实现者可以更加主动地去运用它们。
3.1运用实现原则的例子:
更新TCAM
下面用一个例子来说明为什么要使用原则。
使用TCAM进行IP地址查找
图3.4是TCAM用于IP地址查找的例子。
IP地址前缀是长度为32比特的三态字符串,通配符都在字符串的尾部。
我们稍微改变一下表示法,使得*表示任意数量的通配符。
按照TCAM的工作原理以及最长前缀匹配的要求,TCAM中的地址前缀必须按照前缀长度从大到小的顺序排列。
路由表是动态变化的,如何在TCAM中添加或删除一条地址前缀,与此同时仍然保持前缀长度从大到小的排列顺序?
假定在图3.4的TCAM中需要添加一条新的前缀11*。
假定TCAM是向上扩展空间的,最朴素的方法是在长度为2的前缀表项中插入前缀11*,比如插在前缀0*的前面,为此,需将前缀10*至前缀010001*整体向上移动一个位置。
对于包含大量路由表项的路由器来说,这种更新的速度太慢了。
有没有快一点的方法呢?
我们再来看一下图3.4的前缀排列方法:
长度相同的前缀排列在一起,按照从长到短的顺序排列,相同长度的前缀还按照大小进行了排序。
事实上,相同长度前缀的顺序对于TCAM执行最长前缀匹配不是必须的,因为一个IP地址不可能同时匹配两个相同长度的前缀。
我们只需要按照前缀长度进行排序,并不需要相同长度前缀之间排序,因此这是一个可以利用的自由度。
理解并利用自由度
(1)
利用该自由度,我们可将11*插入到00*和111*之间。
当然,如果为此将111*及以上前缀向上移动一个位置,则不会有多大优化的效果。
我们的想法是将111*移出,将11*插入111*的位置,然后再为111*寻找一个插入位置。
尽管这个问题和原来的差不多,仍然要向TCAM中插入一条前缀,但是问题的规模缩小了一点。
理解并利用自由度
(2)
我们可以将这个方法推广:
……。
显然我们可以采用递归的思想来设计一个算法。
使用算法技术—采用递归
实现的时候展开递归:
……。
如果每一种长度的前缀都有(即最坏情况),需要(32-i)次访存。
对于较小的i,访存次数接近32。
这个算法已经比朴素的算法好了很多,但是不是最好了呢?
还能减小最坏情况下的访存次数吗?
(想一想)
我们再来看这张图,这张图假设空闲空间在TCAM的顶部,实际上空闲空间可以放在TCAM的任何地方,因此空闲空间的位置也是一个设计自由度。
进一步利用自由度
利用这个自由度,可以将空闲空间放在中间,比如放在长度为16的前缀项后面,这时最坏情况下的访存次数可以减少一半。
当然,空闲空间的数量也可以是一个自由度,可以进一步减少最坏情况下的访存次数。
除了空闲空间的位置及数量之外,还有没有可以利用的自由度了呢?
提示:
长的前缀是否一定要出现在短的前缀之前?
比如,010*能否放在111001*之前?
完全可以,因为不可能有一个地址会同时匹配这两项。
一个更复杂的自由度是,“如果i>j,那么长度为i的前缀必须出现在长度为j的前缀之前”,这是一个充分条件,但不是必要的。
一个不那么严格的要求是,“如果两个前缀P和Q可能匹配同一个地址,且P比Q长,那么P必须出现在Q之前”。
这样修改规范后,可以进一步减少最坏情况下的访存次数。
缺点是提高了复杂度。
尽管以这么复杂的方法来减少访存次数有点不划算,但它指出了一个重要的原则,即放宽要求。
我们经常将一个大的问题划分为较小的子问题,然后将子问题及相关的规范交由相关人员去解决。
比如,TCAM的硬件设计人员可能将更新问题交给写微代码的人去完成,要求将长的前缀放在短前缀的前面。
但是这个规范可能不是解决原始问题的唯一方法,改变规范(P3放宽要求)可能产生更有效的解决方案。
当然,这需要有具有好奇心和自信的设计人员,他理解整个大问题,并且足够勇敢来提出危险的问题。
这个例子其实是告诉我们,要去寻找和利用自由度来优化实现。
3.2算法vs算法学
如果认为这个例子基本上是算法层面上的,不要求系统思维,那么下面再举一个例子来说明算法与算法学的差异。
举例:
安全物证问题
假设一个入侵检测系统通过统计流量来检测异常节点,比如在一个测量周期内发现一个节点向网络中的不同机器发送了10万个包,就确定这个节点在进行端口扫描攻击。
当判定某个节点为攻击源时,要将该节点在测量周期内发送的包写入安全物证日志,供管理员进一步分析。
如何检测攻击不是我们要讨论的,我们关心的是当判定某个节点为攻击源时,如何得到它已经发送过的那些包。
解决方案
为实现此目的,我们维护一个包队列,当一个数据包被转发时,路由器将该包放入队头。
为限制队列的长度(路由器的内存是有限的),当队列满时,删除队尾的包。
该方案的主要困难是,当检测到一个可疑流时,该流已经有大量的数据包在队列中了,并且和大量其它的包混在一起。
如何高效地从队列中找到属于可疑流的所有包?
最相素的方法是顺序搜索队列,这显然非常低效。
我们一般会采用什么方法,快速找到流F的数据包呢?
教科书上的算法
教科书上的算法会构造某个索引结构来快速搜索流ID。
比如,我们可以维护一个流ID的哈希表。
……
但是该算法仍有问题。
它需要额外的空间来维护哈希表和指针列表,而对于高速实现来说存储空间是非常宝贵的。
它还增加了包处理的复杂度,需要维护哈希表。
这个算法的问题在哪里?
就是我们潜意识里假设每一个流都有可能是可疑流,因此,每一个包到来时都将其分到相应的流中。
这样,一旦某个流被认定是可疑流,立即就可以得到属于该流的所有数据包。
但事实上,可疑流只是少数,这里面我们做了许多无用的处理,将属于正常流的包也都组织在哈希表中了。
比如,10万个流中只有一个流是异常的,但我们将10万个流的包都做了归类,这是显而易见的浪费。
那么我们怎么可以避免做无用功呢?
联想到我们前面举过的例子(检测异常URL),我们可以采用推迟计算来解决这个问题。
系统的解决方案
基本思想:
将判断一个包是否属于可疑流这件事情,拖到不得不做(发现可疑流,且包将移出队列)时再去做。
什么时候不得不做?
(1)发现了可疑流;
(2)数据包将移出队列
令路由器转发包时统计每个流(节点)发送的包数。
当流F发送的包数超过一个阈值(如10万)时,将流F添加到可疑节点列表中。
当可疑节点列表非空且队列满时,每当往队列中添加一个包,就从队尾中移出一个包,判断该包是否属于F。
若属于F,将该包(或指针)拷贝到物证日志中;若否,将该包丢弃。
该方案不需要维护哈希表和指针列表,节省了大量的存储空间和计算开销。
3.3十五条设计原则
第一章的例子(设计一个检测异常URL的芯片)和前面这两个例子给了我们有关网络算法学的一些初步体验,下面介绍作者归纳的15条原则,这15条原则我们会不断地使用。
这15条原则可以分为三类:
1)系统原则:
第1-5条原则利用了系统思想,它将系统看成是由子系统构成,而不是一个黑盒子。
2)兼顾模块化和效率:
第6-10条原则允许保留复杂系统的模块化,与此同时给出提高性能的方法。
3)加速:
第11-15条原则提出了加速某个关键功能的技术(仅考虑该功能本身)。
每条原则会用一个例子来说明,具体细节将在后续章节中讨论。
P1:
避免常见情形中的明显浪费
在一个系统中,在一些特殊的操作序列中可能存在资源浪费。
如果这些模式经常出现,就有必要去除这个浪费,这样可以从整体上降低系统的开销。
编译器的例子:
消除重复的子表达式。
经典的网络例子:
操作系统和用户空间之间多次数据包拷贝(第五章介绍)。
这条原则看起来容易做起来难,最难的是如何发现明显的浪费。
这里,每一步操作孤立地来看没有浪费,是由于特殊的操作序列导致了浪费。
显然,暴露的上下文越大,越可能发现产生浪费的操作序列,因而这里需要系统思维。
P3:
放宽系统要求
当一个系统从上到下进行设计时,先将功能在子系统间划分。
在确定了子系统的需求和接口后,再设计每一个子系统。
当遇到实现困难时,有些方面的要求要改变。
如图3.8所示,一个系统由两个子系统组成,子系统2对子系统1的实现要求称为规范S。
如果子系统1实现困难,有时可以降低对其的实现要求,要求子系统1遵循较为宽松的规范W。
在第1章的例子中,由于设计除法电路(subsystem1)比较复杂,改用移位代替除法,也就是放宽了子系统1的实现规范(S->W):
子系统1只需实现移位操作而不是任意的除法操作。
其代价是子系统2必须遵循更强的特性(P->Q):
门限须用2的幂次表示,而不是一个任意的浮点数。
P3a:
牺牲确定性换时间
超级节点的检测:
确定性的方法是统计以每一个节点作为源(或目的)的数据包数量,这需要检查每个包的源地址(或目的地址)进行统计,这在高速网络中是做不到的。
随机化方法是每隔一定数量的包统计一次,虽然不能保证百分之百正确,但在很大概率上是正确的。
P3b:
牺牲精度换时间
图像压缩是一种很耗时的操作,对于实时视频要求压缩速度很快。
离散余弦变换可将信号能量集中在少数几个变换系数上,然后只需对主要的变换系数进行量化编码,达到快速压缩的目的。
但是图像质量(精度)会受到一些影响。
P3c:
在空间中移动计算
在空间中移动计算是指将计算从一个子系统移动到另一个子系统(注意和前两条原则的不同)。
比如,数据包分片会影响路由器的吞吐量,IPv6将分片的功能从路由器移到了源节点。
为此,源节点主动探测端到端路径上的MTU,确定合适的分组大小,路由器不再提供分片的功能。
这样就简化了路由器的实现。
当然,这里的移动已经超出了一个网络设备的限制,是在整个网络中的移动(如果将网络看成一个系统,则路由器、主机就是子系统)。
P4:
利用系统组件
系统设计采用黑盒视图,就是将系统分解为若干子系统,然后独立地设计每一个子系统,而不去关心其它子系统的内部细节。
尽管这种自顶向下的方法具有很好的模块化,但实际上,性能关键的组件在构建时通常部分地采用“自底向上”的方法构建。
比如,要在一个硬件上设计算法,通常要让算法适应硬件的特性,而不是让硬件来适应算法。
P4b:
用空间换速度
一个显而易见的技术是用较大的空间来减少计算时间,比如将函数计算变为查表。
一个不那么显而易见的技术是用较小的空间来提高速度。
比如,把一个很大的数据结构压缩到可以放入cache,或者大部分可以放入cache,可以极大地提升系统速度。
当一个表的规模很大时,该技术特别有效。
有人可能会问,空间和时间都节省了,怎么会有这样的好事?
事实上,计算复杂度是增加的。
比如,访问压缩的数据结构,其处理复杂度是提高的。
但由于现代处理器的计算速度比访存速度快得多得多,在多核处理器上这个差距更大。
在这种情况下,适当增加计算复杂度并不会使处理器成为瓶颈,却因为提升了访存速度使得系统整体性能得到提升。
这是实践中用得很多的一种优化技术。
这也启示我们,在优化系统时一定要搞清楚瓶颈在什么地方,如果计算不是瓶颈,那么减少计算并不能提高系统速度。
如果P4原则使用过度,系统的模块化将受到损害。
为此,需要注意两个问题:
(1)如果我们利用其它系统特性只是为了提高性能,那么对那些系统特性的改变应当只影响性能,不影响正确性。
(2)我们仅对确认为是系统瓶颈的组件运用该技术。
以上表明,对系统的优化要有一定的度,不是越多越好。
对系统修改越多,可能产生的不可预知的相互作用就会增多,可能会对系统的正确性产生影响,所以应当只对系统做最小的修改(不提倡大刀阔斧的改)。
P5:
增加硬件提高性能
当所有的方法都不奏效时,增加硬件(如使用更快的处理器、存储器、总线、链路等)可能是更简单和有效的方法。
然而,硬件毕竟成本比较高,即使需要增加硬件,我们也希望只增加最少的硬件。
这就要求充分挖掘软件的潜力,把软件的性能做得极至。
随着处理器的速度越来越高,存储容量越来越大,特别是多核处理器的出现,我们发现软件算法设计得好的话,也是可以运行得很快的。
因此,一种比较理想的情况是,用软件实现关键的算法,然后通过处理器的升级自然获得速度的提升。
当然这不是一件轻而易举的事。
将功能在硬件和软件之间划分是一门艺术。
一般认为,硬件(不包括处理器)缺乏灵活性(不容易增加新的功能),且设计成本高、周期长,适合完成较简单的、较固定的功能。
软件灵活性好,可以很容易地移植到新的、更快的处理器上,获得性能提升。
但是动态可重构技术的出现使得这种界线在逐渐模糊,利用动态可重构FPGA实现的系统,既有硬件的执行速度,又有软件的可编程性,可以执行复杂的功能(可重构处理器),设计工具的出现也使得硬件设计周期大大缩短了。
当然现在用FPGA构造系统还比较贵,成本可能会是一个考虑的因素。
总之,在是否增加硬件、增加什么硬件方面,我们的决策是随着技术的发展而变化的。
以下特殊的硬件技术通常用于网络ASIC芯片中。
P6:
用高效的定制例程替换低效的通用例程
通用性和高性能通常是一对矛盾。
通用性要求兼顾大多数情形,一般来说就不会针对某种情形进行特殊设计。
所以,一个“放之四海皆宜”的东西一定不是最高效的。
比如,操作系统的cache替换策略是LRU,将最长时间未被访问的数据记录替换出去,这么做符合一般的程序运行模式(局部性原理)。
然而,考虑一个查询处理例程,它在处理一个数据库查询请求时,需要依次地处理一系列的数据库记录。
在这种情形下,最近使用过的数据库记录恰恰是最不会被再次访问的,因此应当选择这样的记录替换出去。
正因为如此,许多数据库应用都用定制的缓存例程替换了操作系统的缓存例程。
为避免代码膨胀,最好只对关键的例程进行定制化。
P7:
避免不必要的一般性
设计抽象的、一般性的子系统可能导致不必要的或者很少使用的特性。
我们可以移除一些不必要的特性来提高性能,当然这要求例程的使用者能够接受该限制。
P8:
不要受参考实现的束缚
我们实现一个协议或系统时,都要遵循相应的规范。
规范是解释性的,主要解释概念、功能、流程等,并不包括实现。
规范应当使用规范语言来书写,但是因为规范语言用得不普遍,许多规范会用命令式语言(如C语言)给出,即参考实现。
这些参考实现给出了如何实现功能的代码。
这会带来两个副作用,一是描述过于详细(规范应当只是说明要做什么,不应包括怎么做),二是实现者可能直接将参考实现代码拷贝到自己的系统中。
但是,参考实现代码只是为了解释概念,并不关注效率,因此直接使用参考实现会非常低效。
P9和P10:
这里暂不举例(没有找到可以三言两语讲清楚的例子),下一章结合具体的例子再解释。
P11:
优化预期情形
尽管系统可能会呈现出很多种行为,但是大部分情况下,系统的行为是可预期的。
比如,一个设计良好的系统大部分时间工作在无故障的状态;网络中的数据包大部分情况是按序到达的,并且没有出错。
我们有必要去优化这些常见的情形,哪怕使得非预期情形的处理变得低效。
优化常见情形的方法通常称为启发式方法。
启发式方法一般不被理论家待见,他们更倾向于那些能够用平均指标或最坏指标精确量化的机制。
然而,在实际的计算机系统中,启发式方法被大量使用。
(性能才是硬道理)
确定常见情形主要依靠设计人员的直觉,也可以通过测量工具来发现。
通常,每个数据包都要进行的操作可视为常见情形。
P12:
增加或利用状态
如果一个操作的代价很高,可以考虑维护额外的(冗余的)状态来加速该操作。
数据库中的一个典型例子是使用辅助索引。
比如,银行记录可能使用客户的身份证号作为主键进行存储和查找,但是很多时候需要根据客户名字进行查询。
为了加快这种操作,需要利用客户名字建立另外一个索引(如哈希表、B-树)。
维护额外的状态意味着当状态有变化的时候,额外的状态也要更新。
有时候可以不增加新的状态,而是利用已有的状态,这种技术称为增量计算。
比如,在计算IP头检验时,头部只有几个域有变化,可以进行增量计算(不需要对所有的域计算检查和)。
P13:
优化自由度
知道哪些变量在我们的控制之下,对于我们进行系统优化是很有帮助的。
我们的问题就变成了:
优化这些变量以最大化性能。
在这里变量就是自由度,就是可以由我们改变的因素。
使用多分支trie优化IP地址查找就是一个例子。
这里有两个自由度:
(1)不同层的步长(检查的前缀位数)可以不同;
(2)每一层上检查几个比特可以通过动态规划来确定(对于给定的吞吐量,最小化内存的需求)。
P15:
利用算法技术构造高效的数据结构
在有些情形中,高效的算法肯定可以极大地提高系统的性能。
但是必须注意,在任何算法问题成为瓶颈前,需要先运用P1~P14原则对系统进行优化。
换句话说,在其它问题没有解决前,算法通常不是瓶颈。
因此我们在科研实践中,通常先考虑数据路径上其它方面的优化。
在所有其它问题都解决后,剩下的才是算法问题。
经常有学生觉得,不做算法就不算做研究,做的工作就没有技术含量。
事实上,真正的高手解决问题往往是四两拨千斤,用最少的修改把问题解决好。
比如,通过改变几行代码的顺序就明显提高系统性能(只是这样的工作写起论文来不如算法漂亮)。
在系统领域,简洁优雅的解决方案最受高水平会议的青睐,因为这样的工作离实用最接近。
所以我们在做系统优化时,遵循的一个原则是修改越少越好;在达到相同效果的前提下,方法越简单越好。
这也是操作系统中很少用复杂算法的原因,太复杂的算法会影响系统的稳定性和可靠性。
算法方法包括使用标准的数据结构,以及一般的算法技术,如分治和随机化。
然而,算法设计者必须做好这样的心理准备,就是其设计的算法可能随着系统结构和技术的变化而被淘汰。
真正的技术突破可能来自算法思维的运用,而不仅仅是重用已有的算法。
3.4一些警告
在运用原则前,必须理解重要的性能指标,确定系统瓶颈。
在完成改进后,必须通过实验来确认改进的效果。
下面用两个例子进行说明。
案例一:
减少网页下载时间
在图3.8中,web客户欲从服务器获取内嵌有图像的一个网页。
典型地,客户先发送对网页的GET请求,然后针对内嵌的每个图像分别发送GET请求。
对原则P1(消除明显的浪费)的自然运用是:
为什么服务器不自动将图像和网页一起发送给客户,而要等待客户的请求呢?
这应当可以减少网页的下载延迟。
为了测试这个猜想,作者修改了服务器软件,令服务器自动发送图像给客户,然后测量性能。
出乎意料的是,网页下载延迟的改进很微小。
使用一个基于tcpdump的网络分析工具,作者发现有两个原因导致这个看似不错的改进方法是个坏主意。
(1)与TCP的相互作用:
web传输建立在TCP之上。
TCP在新建立的连接上使用慢启动发送数据包:
首先发送一个TCP段,在收到确认后发送两个TCP段,在收到ACK后逐步提高发送速度。
因此,即使令服务器主动发送图像,发送端TCP也必须等待ACK来提高发送速度,这和等待客户的图像请求没有多大的区别。
(2)与内容缓存的相互作用:
许多客户端有缓存功能,一些常用的图像会缓存在本地,因此令服务器主动发送已经缓存在本地的图像是对带宽的浪费。
如果让客户发送请求,则客户对于已经缓存在本地的图像不会发送GET请求。
从这个例子我们了解到,系统优化的困难之一是系统的各个部分之间存在相互作用。
如果对这些相互作用没有清楚的了解,则优化的效果可能达不到我们的预期。
但这里最困难的地方也就是很多时候不能清楚地理解复杂的相互作用,这时候实验验证就很重要了。
案例二:
加速基于特征的入侵检测
许多网站安装有入侵检测系统,目前最流行的入侵检测系统是snort。
Snort根据系统中安装的一组规则来检测攻击。
图示为snort的一条规则,包括如下几个部分:
●动作:
包括pass、alert、log三种。
●协议:
包括TCP、UDP、ICMP三种。
●源网络:
通常是除本地网络之外的所有网络。
●源端口:
本规则为any。
●目的网络:
通常是本地网络
●目的端口:
本规则为80。
●Msg:
如果匹配该条规则,则给出该条提示。
●Flags:
匹配该规则的TCP包,这些标志必须置位。
●Content:
包载荷中包含的特征字符串,可能不止一个。
●Nocase:
表示特征串是大小写无关的
和防火墙的过滤规则只匹配IP地址、端口号、协议、TCP标志位等不同,snort的规则还要匹配数据包载荷中指定的字符串。
经统计,84%的snort规则中包含字符串。
实验表明,字符串匹配是snort中开销最大的操作,占整个执行时间的31%。
Snort匹配规则的方法
作者发现,匹配某个过滤器的规则数可能很多,比如匹配80端口的规则多达310条(大量攻击是利用web进行的),逐条规则地运用BM算法查找字符串,开销很大。
P1原则的简单应用
作者将规则集中所有的字符串抽出来组织到一个自动机中,修改了BM算法,使得用包内容作为输入,一遍扫描就能找出所有匹配的字符串(给出字符串编号)。
作者将这个新算法应用到snort的整个规则集上,发现其性能为snort中算法的50倍。
然而,当作者将这个新算法集成到snort中,并用一个包踪迹文件(用tcpdump从网络中抓真实的包,存在文件中)进行测试时,发现新算法在性能上几乎没有改进!
作者仔细分析后,认为有两个方面的原因可以解释该问题:
(1)实验使用的包踪迹文件包含了很少的web流量,在匹配了过滤器后只有很少的数据包还要匹配多条规则,因此多字符串匹配在该踪迹文件上不是瓶颈。
当作者换用仅包含web流量的包踪迹文件进行测试时,性能提高非常明显。
也就是说,算法性能与流量模式有很大的关系,不是所有时候都有效。
(2)cache的影响:
多字符串查找需要使用一个数据结构(如trie),其大小随字符串数量的增大而增大。
研究发现,当字符串数量超过100条时,数据结构已经大到无法放入cache中,而Snort中包含的字符串数量达到几千条。
也就是说,算法性能还和规则集的大小有关,仅仅将单字符串匹配换成多字符串匹配,并不能一定提高性能。
教训:
有时候声称的改进可能没有针对真正的瓶颈(如大多数情况是单串匹配),也可能与系统的其它部分有相互作用(如cache)。
八个提醒注意的问题
从以上两个案例,我们提出以下8个需要注意的问题,避免不恰当地运用实现原则。
Q2:
这确实是一个瓶颈吗?
80-20规则表明,80%的性能提升来自于对系统20%的优化,因此只需要找到最关键的瓶颈就行了,其它次要的问题可以不用管。
这可以使用如Oprofile这样的工具来发现。
Q4:
初步分析表明会有重大的改进吗?
在动手进行一个完整的实现前,快速地分析一下可以获得多大的收益,以避免做无用功。
对于终端系统而言,由于访存是最大的瓶颈,因此用访存次数做为粗略的估计是合理的。
Q5:
值得增加定制硬件吗?
通常认为定制硬件设计周期长,开发成本高。
随着通用处理器性价比的不断提高,一种非常有吸引力的做法是用软件实现算法,依靠通用处理器来获得高性能,避免定制硬件。
但是,随着高效的芯片设计工具的出现,定制硬件的设计周期在缩短,批量生产也极大地降低了成本(与通用处理器相比),所以也有越来越多的公司将网络功能放到芯片中实现。
Q6:
能够避免协议的修改吗?
这些年来,当一些协议运行较慢时,就有人怀疑是协议设计得不好,从而去研究可以提高性能的新协议。
比如,上世纪80年代,有人认为TCP协议很慢,并提出了一个可用硬件实现的XTP协议。
这项工作刺激了TCP/IP开发人员去研究TCP的高效实现,最终导致快速TCP实现在标准的BSD操作系统中发布。
为解决IP地址查找慢的问题,有研究工作提出修改IP协议、加入标签和流交换机制。
这项工作激发人们去研究快速IP地址查找。
如果仅是为了提高性能而修改协议,我们不妨去研究如何高效地实现协议。
Q7:
原型系统验证了