Language-Directed Hardware Design for Network Performance Monitoring

摘要

现在的网络性能监控能力受到现有的交换机对监控范围支持的限制,迫使运营商严重依赖于对网络核心设备可视性差的终端。交换机供应商对交换机增加了更多的监控功能,但是鉴于网络运营商的不断变化的需求,添加特定功能的方法是不可持续的。因此,我们寻找一种交换机硬件原语来支持网络性能监控的表达性语言。我们相信,由此产生的交换机硬件设计可以解决当前和未来的各种性能监控需求。

我们提出一种性能查询语言,Marple,它以熟悉的功能结构为模型,如map,flter,groupby和zip。 Marple在交换机硬件上支持可编程键值存储基元。密钥值存储器按照线路速率进行灵活聚合(例如,每个数据包的排队延迟的移动平均值),并且扩展到数百万个键值规模。我们提出了一个Marple编译器,其目标是P4可编程软件开关和用于高速可编程交换机的模拟器。 Marple可以表示以前只能在终端主机上运行的交换机查询,而Marple查询只占用交换机硬件资源的一小部分。

一 引言

对于大型网络的有效性能监控对快速定位网络问题是至关重要的,如高延迟时间,网络链路负载不平衡等问题。网络监控的常见方法是从终端的网络缓存中收集信息,或使用端对端探针来诊断性能问题。虽然终端可以监视到应用层的问题,但是它们在网络链路层的问题是无法进行判断的。例如,对于特定交换机进行本地优化队列建立或导致队列建立的精准流量具有挑战性,迫使运营商间接的推断是网络级的问题。

基于交换机的监控可以使运营商能够更加直观地了解性能统计信息来诊断问题。 然而,传统的交换机机制,如抽样[7,21],镜像[8,42,65]和计数[34,49]是非常受限的。采样和镜像缺少对感兴趣的事件的记录,因为它不可能收集所有数据包的信息,而计数器只跟踪流量统计。这些机制都不会提供相关的性能数据,如排队延迟。

一些先进的技术意识到更好的交换机性能监控的重要性。入栈网络测量技术在数据包本身写入数据包经历的排队延迟。 Tetration芯片[9]提供了一个流量缓存,用于测量流量级别的性能指标。这些指标是有用的,但它们被指定为确定的粒度(例如,每5元组),并且度量本身被固定了。例如,度量的列表包括流水平等待时间和分组大小变化,但不包括延迟变化,即抖动。

运营商的要求是不断变化的,重新设计硬件是昂贵的。 我们认为,添加制定功能的方式是不可持续的。 相反,我们主张构建性能监控原语,可以灵活地重用于各种需求。 可编程交换机现在支持灵活解析,标题处理[33,56]和调度[57]。我们的目标是将网络监控添加到此列表。

受到早期设计硬件支持高级语言设计的启发[36,50,59],本文将语言导向的硬件设计应用于灵活的性能监测问题,。特别地,我们设计了一种语言,可以表达广泛种类的性能监控实例,然后设计高速交换机硬件原语来服务于这种语言。通过设计硬件来支持表达性语言,我们相信最终的硬件设计可以满足各种各样的当前和未来的性能监视需求。

图1概述了我们的性能监测系统。为了使用该系统,运营商使用Marple语言写一个查询(或者是为长期运行的监视器来统计数据(例如:,检测TCP超时),或解决一个特定的问题)该查询被编译成一个可以可编程的交换机上运行的交换机程序,用我们为Marple服务设计的新交换机硬件原语扩充。交换机将结果输出到收集服务器,在那里,运营商可以检索查询结果。我们现在简单地描述一下我们系统的三个组成部分:

查询语言、交换机硬件和查询编译器。

性能的查询语言

Marple使用熟悉的功能结构,如map、filter、group by和zip进行性能监视。Marple提供一个流的抽象概念,其中包含每一个队列中的每一个数据包信息。程序员可以将注意力集中在使用filter来寻找关注的数据包的信息,使用groupby来查找数据包集群信息,使用map计算新的无状态数量,并使用zip来检测同步的性能条件。

用于性能查询的硬件设计

一个简单的Marple实现可以将每个包的元数据从网络流到一个中心位置,并对其运行流查询。现代扩展数据处理系统支持每秒100 k-1m操作每核心,但一个处理速度为1 Tbit / s速度的交换机处理一个1K大小的数据包需要每秒运行1M次操作,比现有系统高2-3个量级。

我们在网络监视中使用高速可编程交换机作为高优先级的设备,因为他们可以通过编程操作多tbit / s数据包流。早期的过滤和灵活的交换机聚合在交换机上大大减少了每秒的记录的数量,以一个标准的数据处理系统运行信息收集服务器。

虽然可编程的交换机支持许多Marple的无状态语言结构,但每一种结构仅能修改单个数据包的组成(例如:,map和filter),它们不支持多个数据包之间的状态聚合(例如:groupby)。为了支持灵活的数据包聚合,我们在硬件上设计一个可编程键值存储结构,那里的key代表流标识符,值代表通过聚合功能得到的数据流的信息。这个键值存储必须以每个时钟周期1个包的速度进行更新(1Ghz),并支持数百万个键(即流)。不幸的是,SRAM和DRAM都不能同时满足快速和高密度的要求。

我们将key - value存储系统分成一个小而快速的芯片缓存SRAM和一个大但慢的DRAM的芯片存储。传统的缓存会因为未命中导致写延迟的不同,然而,数据包转发需要确定的延迟保证。我们的设计实现了了这一点,如果它已经被放入到后备存储器中,它决不会将一个值读入缓存。相反,它将缓存未命中看作是来自新流的数据包的到达。当流被处理完成后,我们将流的值合并到在后备存储中的流的旧值中。因为合并发生在关键的包处理路径上,所以后备存储可以在单独的收集服务器上的软件中实现。

虽然并不总是可以聚合功能而不损失准确性,但我们定义了一类精炼的聚合功能,我们将其称为线性状态,以实现准确的合并。许多有用的聚合功能都是线性的,如计数器,指定类型数据包计数器(例如:只统计超时的TCP包),以及处理指定窗口期的数据包的功能。我们设计了一个交换机指令来支持线性状态的功能,发现很容易满足1Ghz的频率,同时占据的硅区域也较为合理。

查询编译器

我们设计一个编译器可以读取marple查询语句并将其编译成交换机的配置。主要设计目标有两个:(1)P4行为模型[19],一个可用于端到端评估的开源的软件可编程交换机(2)可用于高速可编程交换机硬件模拟器,可以使用不同的指令集进行试验。Marple编译器在输入查询中检测出线性状态的聚合,并成功地实现我们添加到Banzai的线性-状态开关指令。

评价

我们展示了Marple可以有效的表达各种性能监控示例,比如检测和本地化TCP incast,并度量已过期的TCP包的传送情况。Marple查询需要4到11个管道阶段,这对于32个阶段的交换机管道来说是很有限的[33]。我们使用路径驱动的仿真来评估键值存储的性能。对于64 Mbit片上缓存而言,它占据了64×10-Gbit / s交换机芯片大约10%的面积。我们估计,从单个top-of-rack缓存回收速度的交换机可以由一个运行radish的8核服务器进行处理。我们通过两个Mininet案例研究来评估Marple的可用性,这些研究使用Marple来诊断高延迟。marple是开放源代码的,可以在http://web.mit.edu/marple下载。

二 marple 查询语言

本节描述Marple查询语言。第三部分讨论语言构造的交换机实现,而第四部分则描述编译器。Marple提供了一个网络范围性能信息流的抽象。当数据包进入和离开队列时,网络中每个队列的每个包的信息都会保存,流中的元组包含性能元数据,例如队列长度和时间戳。网络操作符在此流上编写查询,就好像整个流都是由一个运行查询的假想服务器处理的。实际上,编译器将网络上的查询分块,并在每个交换机上执行每个部分。

Marple程序使用熟悉的功能结构(filter、map、groupby和zip)来处理性能流,所有这些都以流作为输入,并生成流作为输出。这个函数式语言模型足以支持不同的性能监视用例,但仍然足够简单,可以在高速硬件中实现。Marple的语言结构如图2所示。

数据包流性能

作为基本输入流的一部分,我们称之为pktstream,Marple为每个队列中的每个包提供一个数组,包含以下元素:

(switch, qid,hdrs, uid, tin, tout, qsize)

switch和qid表示数据包所在的的交换机和队列。包可以在单个交换机中遍历多个队列,因此我们提供不同的项。常规的包头(以太网、IP、TCP等)都可以在hdrs集合中使用,它的uid可以唯一地确定数据包。

数据包性能流提供对各种性能元数据的访问:tin和toiut表示数据包的入栈和出栈时间戳,而qsize则表示数据包所在队列的队列深度。使用两种时间戳来检测属于不同流的数据包是有益的。此外,使用队列大小是有好处的,因为我们不能总是从两个时间戳上确定队列大小:一个链接可能服务多个队列。

pktstream中的元组是按照包的出栈时间的顺序处理的,因为这是pktstream中所有的数组元素都知道的最早的时间。如果一个包被丢弃了,tout和qsize都是无限的。可按任意顺序处理对应于丢弃的包的数组。

Restrictingpacket performance metadata of interest

考虑指定队列Q、交换机S的高延迟数据包的实例。这是通过查询表示的结果:

 

fileter限制了函数中部分元素之间的关系。filter有filter(R,pred)的形式,其中R是一些包含性能元数据的流。而filter假设pred可能涉及包头、性能元数据,或者两者兼而有之。filter的结果是另一个只包含满足假设的数据组的流。

Computingstateless functions over packets.

Marple允许用户在传入的流中计算数组的功能。一个简单的例子就是将包时间戳四舍五入到一个“区间”:

map操作对“tin /epoch_size”的表达式进行重新计算,这是在流中可用的数组元素上进行运算,并生成一个新的元素的过程。这个构造的一般形式是map(R,[expression],[field]),通过输入流的元素数组建立了一个新的输出流的元素数组。

在多个包上有状态地聚合

Marple允许在用户指定的粒度上对多个元组进行聚合统计。例如,对属于每个传输层流的数据包进行查询计数:

在这里,groupby将传入的pktstream按照传输5元组的方式划分成子流,然后应用聚合函数计数来计算每个子流中的元组的数值。Marple允许用户在每个子流的元组上编写灵活的聚合函数。例如,用户可以通过按指数加权移动平均(EWMA)来跟踪每个连接的延迟峰值:

在这里,聚合函数ewma使用avg的当前值和传入的包时间戳来获取一个ewma avg。与前面的示例不同,EWMA聚合函数依赖于正在处理的数据包的顺序。

groupbys采用一般形式的groupby(R,[aggFields],fun)。这个构造的灵感来自函数式编程的折叠[45]。在现有的查询语言中,这种依赖于顺序的折叠的表达是具有挑战性的。例如,SQL只允许确定顺序的交换聚合,不管它是内置的还是用户定义的。

聚合函数fun是具有两个参数:一个状态变量的列表、一个入栈数据包数组的列表。Fun函数的定义可以是如下几种方式:表达式 (x = ...);分支表达方式 (if pred {...}else {...});groupby中控制输出流的emit()形式。下面,我们将展示一个用于检测新连接的聚合示例:

groupby的输出是包含聚合数组(例如:,5元组)和聚合值(例如fcount)的流。输出流只包含指定的元组,即那些在聚合函数执行时emit()函数所包含的元组。例如,new_flow的输出流由每个新的传输层连接的第一个包组成。如果函数没有emit()s,则用户仍然可以读取聚合字段和它们当前的聚合状态值作为表。

多个查询的链接

由于所有的Marple都构建和使用流,因此Marple允许用户编写查询,这些查询将先前的查询结果作为输入。一组元组从一个查询流到下一个查询,每个查询可以从传入的元组中添加或过滤信息,甚至完全删除元组。例如,下面的程序跟踪流的大小分布,即从相同的5元组中,由超过一个固定的时间量增量delta分隔开的数据包。

fl_detect函数使用相同流被发现的时间来查找新的流的位置。因为emit()声明的位置,从fl_track中获得的flowlet大小只在看到新流程的第一个包时流到其他操作符中。

 

Map函数 fl_bkts将fl_track所释放的流的数量大小放入一个bucket索引中,该索引用于计算fl_hist对应的bucket中的flowlet数量。

通过查询添加结果

Marple提供了一个zip操作符,它连接两个查询的结果,以检查两个条件是否同时保存。考虑从多个连接到单个队列的数据包的输入实例,这也是TCP协议的典型事例。这可以通过结合两个不同的条件来检查:(1)在短时间间隔内的活动流的数量是高的,并且(2)队列占用很大。

用户可以使用两个聚合来计算当前时间区间中活动流的数量。

 

这个时间区间中的活动流的数量可以通过zip操作符将原始包流中的队列占用信息相结合。

在两个输入流上的zip操作的结果是一个包含元组的流,这是两个流中的所有元组元素的连接,只要两个输入流包含从相同的原始数据包中处理的有效元组。zip是一种特殊类型的流连接,其结果可以在无需两个流的同步情况下进行计算,因为两个流的元组都源自pktstream。zip的结果可以像其他流一样处理:结果查询中的filter检查上面的两个前提条件。

我们不需要更多的一般形式的连接,类似于像CQL[30]这样的流查询语言。流连接具有相当复杂的语义,可以产生特别大的结果。因此,Marple将用户限制为简单的zip连接。

我们在图7中展示了几个Marple查询的例子。例如,Marple可以表达简单计数器的测量、各种形式的TCP重排序、高损耗连接、端到端网络延迟的流和TCP 数据包的流入。

Marple查询的限制条件

有些聚合对于在网络范围内实现流的查询具有挑战性。例如,考虑在整个网络中的任何地方都看到的数据包上的一个EWMA,按照tout的顺序进行数据包的处理。即使使用时钟同步,这个聚合也很难实现,因为它要求我们在交换机或将所有信息包传输到中心位置之间进行协调。

Marple的编译器拒绝那些需要在多个交换机上按照tout的顺序处理的多个数据包的聚合。具体地说,我们只允许在这三个条件中中的聚合:

(1)            在每个交换机上独立操作,在这种情况下,我们通过交换机来划分查询;

(2)            独立地在每个包上操作,在这种情况下,我们在数据包经过的下一个交换机上进行聚合来执行协调。

(3)            是关联式和可交换的,在这种情况下,可以将每个交换机的结果组合在一起,以产生整个网络的正确的总体结果。例如一个流中的数据包流经网络的总的次数。在这种情况下,程序员可以用assoc和comm关键字来注释聚合函数。

三 SCALABLE AGGREGATION AT LINE RATE

交换机如何实现MARPLE的语言架构?我们需要交换机上存在以下指令功能以实现:groupby、map、filter、zip的功能。

 

在四种语言结构中,map、filter和zip是无状态的:它们仅对包的数组元素进行操作,不修改交换机状态。这种无状态的操作已经被支持在支持可编程包头处理的新兴可编程交换机上[3,13,25,33]。另一方面,groupby构造需要在交换机上维护和更新状态。

由于两个原因,在交换机上进行有状态的操纵(groupby)是具有挑战性的。首先,高端交换机上在下一个数据包到达之前更新状态的时间预算可以低到1纳秒。第二,交换机需要保留的状态量与聚合记录的数量成比例(例如:每一数据流),它可能随时间而无限增长。我们在硬件中使用可编程的键值存储来处理这两个挑战,其中键表示聚合字段,而值表示由聚合函数更新的状态。我们的键值存储有一个“分割”的设计:一个存储在交换机上的小的、快速的芯片上的键值处理以线性速率传送的数据包,而一个大而缓慢的后备存储器允许我们扩展到大量的流量。

高速开关ASICs通常在多个端口上共用入口管道和出口管道,时钟频率为1GHZ,支持多达10亿64字节数据包每秒的聚合容量[33]。要处理来自1ghz的数据包的状态更新,芯片上的键值必须在存储SIC上的SRAM中。然而,可用的SRAM监测仅限于几十兆比特(约10 k - 100 k的流)。

为了扩展到更多的流,芯片上的键值存储作为更大的外芯片的缓存。在传统的缓存设计中,缓存失败需要访问非确定性延迟的的外部存储来读取存储状态。因为聚合操作需要我们读取值来更新它,所以整个状态更新操作在这个过程中会产生不确定性的延迟。这导致了交换机管道的瘫痪。不幸的是,管道瘫痪影响了在所有端口上提供数据包处理的能力。

我们设计了一个键值存储方式甚至能在cache失效的情况下保证能够线性处理。不同于因为从DRAM中取数据导致的管道阻塞,我们将进入的数据包作为新的数据流的第一个数据包并且将流的状态赋予一个新的初始值。来自相同流的后续包在键值存储中的新创建的流条目中进行聚合,直到数据流被传出。当数据流被传出时,我们在使用合并函数的时候将流的值合并到后面的存储中,并将合并的结果写入后备存储器中。在我们的设计中,开关只写入后备存储器,但从未读取它,这有助于避免不确定性的延迟。如果没有最近的传出,后备存储器可能与芯片缓存不一致。我们通过强制定期重写来解决这个问题。

要将一个流的新聚合值与它在备份存储中的旧值正确地合并,缓存需要维护并将副本发送到后备存储器。一个简单的副本用法是将每个流的每个包的状态存到数组元素中,这样后备存储就可以简单地运行整个包流的聚合函数。然而,在实际的实现中,副本的大小应该是有界的,而不是随着流中包的数量而增长。在接下来的四个部分,我们将描述两类可以与少量的辅助状态合并的查询(§3.1和§3.2),讨论不可以合并(§3.4)的查询,并提供聚合的一般条件,两种可聚合查询的类型并将它们与不可合并的查询区分开来(§3.5)。

3.1相关的状态

一个简单的可的类别聚合是关联函数。假设在状态s上的聚合函数是s = op(s,f),op是一个关联运算,f是一个数据包数组元素。然后,如果op身份元素I和插入流的默认值s0 =I,很容易显示这个函数可以使用函数op(sbacking scache)合并,sbacking和scache存储在主存储器中。关联条件允许我们合并聚合函数,比如addition、max、min、set union和set intersection。

3.2线性状态条件

考虑一下EWMA聚合函数,它维护流中所有包中排队延迟的移动平均值。聚合函数更新EWMA s如下:

我们把s初始化为s0。假设一个流F被从芯片缓存中删除,并被s的EWMA写入后备存储器。流F流出后的第一个数据包当做新流入片上缓存的新的数据流处理,以状态s0作为初始状态。假设来自F的N个数据包命中了芯片缓存,导致EWMA的参数从s0变为scache。然后,正确的EWMA的 scorrect满足:

 

EWMA可以通过如下条件获得:每次状态更新后片上cache存储的;当合并scache 和sbacking时,将、相加。

我们可以将这个实例概念化。让p作为一个向量,存储有从流的最后k个数据包中获得的头信息和性能元数据,K是在查询编译的时刻确定的整数(4.3节)。我们可以将任何聚合函数与状态更新的形式S = A(p)·S + B(p)合并,其中S为状态,A(p)和B(p)为最后k包的函数。我们把这种情况称为线性状态条件。

有界数据包的要求很重要。考虑图7中的TCP非单调查询,它计算的数据包的序列号比到目前为止所看到的最大序列号要小。聚合可以如下表达:

虽然表面类似于(p)·S + B(p),但系数B(p)是maxseq(到目前为止最大的序列号)的函数。从直观上看,由于B(p)不是有界包history的函数,所以需要合并的辅助状态很大。3.5将这些内容形式化。

相反,从图7中略微修改的TCPout- sequence查询是线性的,因为它可以写成

上一个数据包的序列号lastseq,只依赖于最后两个包:当前和前一个包。在这里,A(p)和B(p)是固定范围内数据包的函数,k= 2。

线性状态的合并查询需要交换机存储最初的K个包和最近的k包的键的信息,因为它(re)出现在键值存储中;详情可在附带的技术报告[15]中找到。一个聚合函数是线性的状态,前提是对于函数中的每个变量,状态更新满足线性状态。如果所有的聚合函数都是线性的,则查询是线性的。

3.3可扩展的聚合函数

一个没有emit()和线性(或关联)聚合函数的groupby可以在不丢失准确性的情况下实现可扩展性。此类聚合的示例(从图7)包括跟踪TCP连接中的连续数据包,这些数据包都是无序的,并计算每个连接的TCP超时次数。

如果groupby使用emit()来将元组传递给另一个查询,那么即使它的聚合函数是线性的或关联的,它也不能被有效地实现。emit()输出的是聚合函数的当前状态,它假定当前状态总是在开关的芯片缓存中可用。只有当流还未流出,有效地将键值存储区缩小到其芯片缓存时,这才可能实现。

3.4处理不可扩展的聚合

线性状态和联系可以实现聚合的函数并可实现可衡量的应用,有两个查询的类型我们无法进行衡量:(1)聚合函数查询,既没有关联,也不是线性的和(2)groupby含有emit()语句的查询。

第一种类型的一个例子是前面讨论的TCP非单调查询。第二个类的示例是图7中的流大小查询,其中第一个groupby生成的流的大小flowlet大小进入第二个groupby函数。

对于非可伸缩的查询,有很多变通方法。其一是重写查询以删除emit()s。例如,我们可以重写损失率查询(图7),独立地记录在单独的键值存储中,每个流的数据包和数据包的总数,并且在每次需要查询损失速率时,都由使用者查询两个键值存储。每个键值存储都可以被量化,但是实现是在一个瞬时损失的准确性相对于精确地跟踪每一个包使用一个zip后的损失率。第二,操作人员可能满足于流的值,在两次流出之间的值都是准确的。第三,操作人员可能需要运行一个查询来收集数据,直到芯片缓存已满,然后停止数据收集。最后,如果键的数量足够小到在缓存中(例如:如果密钥是一个应用程序类型,那么系统可以提供准确的结果。

3.5聚合的统一条件

我们提出一种普遍的条件,将可合并的函数与不可分割的函数分离开来。非正式的,可合并的聚集函数是那些在函数状态本身的大小中保持辅助状态线性的函数。这个特性也使联合和线性的状态得到统一。我们现在用几个无证明的定理形式将结果形式化。附随的技术报告[15]包含证明。

让n表示在Marple查询中跟踪的状态的大小:它必须是有界的,并且不应该随着包的数目而增加。在后台存储中,当合并芯片缓存中的scache、主存中的Sbacking时,交换机可以维护并将辅助状态aux发送给后备存储,以正确地执行合并。在EWMA的例子中,是辅助状态。然后,一个合并函数m作为一个聚合函数f是一个满足如下条件的函数:

m(scache,aux,sdf)=f(s0,{p1,…,pN}

对于任何N和数据包序列p1,…pN。F是每一个有序数据包函数的简写。

首先,我们证明了每个聚合函数都有一个合并函数,假定允许他使用大量的辅助数据。

定理3.1。每个聚合函数都有一个对应的合并函数,使用O(n2n)量级的辅助位。

不幸的是,内存是有限的,而Marple不应该使用比用户聚合函数所表示的更多的状态。我们说,如果辅助状态对任何数据包序列都有大小O(n),那么聚合函数是可合并的。这个特性与我们迄今为止所描述的一致:状态线性和结合性的条件确实可以通过这个定义来实现合并。

定理3.2。如果一个聚合函数是线性的或相关联的,它有一个含O(n)位辅助状态的合并函数。

定理3.3。TCP 非单调查询在最坏的情况下需要Θ(n2n)辅助位。

这就引出了一个问题:我们能否确定一个聚合函数是否可以用O(n)量级的辅助位进行合并?我们提供了一个算法(在技术报告中描述),它计算了合并给定聚合函数所需的最小辅助位大小。我们目前的算法辅助位的大小是n的平方量级。

然而,多项式时间算法是不太可能的。 我们通过考虑一个简单问题的来说明这个问题的复杂性,在这个为题中聚合功能M作为输入:给定聚合函数f和合并函数m,m是否能将对所有的数据包的聚合函数m进行聚合呢?

理论3.4。 确定聚合函数能否进行合并是co-NP-hard。

这个结果的实际含义是不太可能一个一般而有效的过程来检查是否有任意的聚合功能可以使用少量的辅助状态合并。因此,识别特定类别的函数(例如,状态线性和关联),并检查聚合功能是否属于这些类是我们希望做的最好的。

3.6 硬件可行性

我们优化硬件设计以适用线性查询,并将其分解成五个组成部分。 每个组件都很好理解:我们的主要贡献是将它们放在一起实施有状态查询。 我们现在详细讨论每个组件。

片上缓存是散列表,其中散列中的每一行表存储一定数量流的密钥和值。 如果一个新的流的数据包的散列流入到一个满的散列表中,最近最少使用的一行被逐出。 每排存储8个数据流的信息并存储其key和value.我们选择8个流程是基于8路L1缓存,这在处理器中非常常见[14]。 这个缓存驱逐政策接近理想但又不太适用的cache失效策略与LRU非常相似。

在交换机数据传输阶段,片上缓存类似于用于计数器的片上哈希表的逻辑接口:每个数据包使用从中提取的key来匹配表中的条目分组报头,并且相应的动作是由交换机来执行的。片上哈希表可用做部署具有特定聚合功能的交换机的高速缓存的路径,以支持更一般的操作以及cache替换策略。

片外后备存储是更大规模的存储key-value的存储装置,例如网络上的信息收集服务器上的存储器。 如第5节所示,用来支持从交换机上的片上缓存输出数据的速率的测量服务器的数量很少的,即使是一个64×100Gbit / s的交换机。

保存数据包history记录

在数据包到达片上缓存的流水线阶段,我们在状态更新操作S = A(p)·S + B(p)之前使用预处理预先计算A(p)和B(p)(有界包的函数)。我们目前设计只处理S,A(p)和B(p)是标量的情况。说A(p)和B(p)取决于最后k个数据包的包字段。然后,这些流水线阶段之前的处理过程像移位寄存器那样起作用并存储来自最后k个数据包的字段。 每个阶段都包含一个读/写寄存器,由到达该阶段的数据包读取该寄存器作为包头的一部分,并写入下一级的寄存器。一旦来自最后k个分组的值已被读入包字段。A(p)和B(p)可以用可编程交换机的无状态指令进行交换。


执行线性状态操作

一旦A(p)和B(p)是已知的,我们使用乘法累加(MAC)[17]计算A(p)·S + B(p)。 这个操作很容易实施:我们的电路合成实验表明一个MAC指令满足1 GHz的时序,;并占据很小的偏上空间。几百平方纳米的交换机芯片可轻松支持几百条MAC指令。

非线性状态的查询

我们使用一组在Domino [56]中开发的用于非线性查询的状态指令来支持非线性查询。 我们的评估表明这些指令对于非线性状态的查询是足够的。

4查询编译器

我们将Marple查询编译成两个形式:P4行为模型[19],emitting P4代码配置; Banzai机器模型,emitting Domino代码配置。在两种情况下,emitted代码配置了一个交换机流水线,其中每个阶段是一个match-action表[33]或我们的键值存储。

编译器对输入查询的初步处理是转换抽象语法树(AST)(图5a)。然后编译:

(1)从全局AST(§4.1)生成交换机本地AST,

(2)从交换机本地AST生成P4和Domino管道配置,(§4.2);

(3)识别线性状态聚合函数,并设置合并这些功能所需的辅助状态用于可扩展实现(§4.3)。为了实施关联聚合函数(§3.1),我们使用程序确定聚合是否是组合的。如果是关联的,则合并函数是聚合本身。

我们使用图4所示的查询作为运行示例来说明编译器的细节。 该查询通过网络中的S1、S2交换机计算出每个时间区间的无需TCP数据包的数量。

 

4.1全网络向本地交换机查询

编译器将对队列中的所有包的全网查询转换为本地查询以生成特定于交换机的配置。 我们分两步实现。首先,我们确定流的位置,即对流中的元组有过修改的交换机序列,最终输出流的查询。例如,被标号为s的交换机过滤过的查询的输出流有一个流位置为set s。其次,我们确定如何使用聚合函数对查询进行分割将对整个网络的查询转换为本地查询。

从最终输出流确定流位置

pktstream的流位置是网络中所有交换机的集合。 过滤器输出的流位置是一组由过滤器确定的交换机。 具体来说,我们通过交换机中的switch== X形式的基本语法来确定对过滤器中输出流的元组修改过的交换机列表。我们使用指令集操作将交换机的指令变化为布尔逻辑。 zip的输出流的位置是两个输入的流位置的交集。通过map和groupby不改变流的位置。

运行示例的流位置如图5b所示。pktstream的流位置是所有网络交换机的集合,但是通过查询中的过滤器仅限于S1和S2(左侧科)。 然后通过zip操作符查询将该位置传播到AST的根。

分割全网范围的聚合

 如§2所述,我们只允许满足三个条件之一的聚合:它们在每个交换机上独立运行,在每个数据包上独立运行,或者是关联的和可交换的。下面我们来描述一下我们如何检查第一个条件,后两个条件的检查方法为:groupby通过uid聚合(条件2)或包含程序员的注释(条件3)。

要检查每个交换机上是否独立运行聚合,我们用每个AST节点标记一个额外的布尔属性,交换机的可分割性,对应于输出流是否已经由它出现的交换机分割。直观地,如果一个流是被交换机分割,我们允许分组顺序相关的聚合作用于该流的多个数据包; 否则我们不会。

通过AST确定和传播交换机分区很简单,基本pktstream不进行交换机分区的。过滤器和zip操作符产生交换机分区的流,如果它们的输出仅出现在单个交换机上。 Groupby产生一个交换机分区流(若该流被交换机聚合)。在所有其他情况下,运算符保留操作数的交换机分区属性。

我们运行示例的交换机分区属性是如图5c所示。 滤波器在两个交换机上产生输出流,因此不进行切换分区。 Groupby通过交换机进行聚合因此是交换机分区的。 在交换分区检查成功后,留下了一套独立的对应于每个AST根操作出现的交换机位置的独立的交换机本地AST。

4.2 查询到管道配置上的AST

这个编译器通过§4.1的交换机本地查询AST生成一个运算符序列。那么这个操作符序列就可以以相同的顺序使用来生成交换机管道配置。在构建管道时需要注意两个方面结构:(1)管道应遵循不同的运算符之间的读写依赖关系,(2)重复的子查询不应该创建额外的管道阶段。 我们生成一个查询AST的后期遍历的序列,保证了一个节点的操作数在操作之前被添加到管道中点。此外,我们从管道中重复数据删除子查询结果以避免在最终输出中重复阶段。 对于运行示例,该算法产生运算符序列:tcps(filter) →

tslots (map) → joined (zip) → oos (groupby).

接下来,编译器从操作符中发出交换机管道的P4代码。 filter和zip配置只涉及到在数据包元数据上设置“有效”位。 map配置向计算表达式传递数据包数组元素。 groupby配置使用一个由聚合字段作为索引的寄存器,并通过在聚合函数中指定的动作进行更新。 我们通过标准程序将marple聚合函数改变为c语言风格的操作。 这允许我们将聚合功能适合单P4操作的代码中.

为了实现Banzai交换机管道模拟器[56],Marple编译器通过连接C风格的代码段释放Domino代码为单个Domino程序。然后,Domino编译器将该程序并将其编译为aBanzai原子管道。 原子是ALU,代表可编程交换机的指令集。 原子实现无状态(例如,增加分组字段)或有状态(例如,原子地递增开关计数器)计算.

4.3 处理线性状态聚合

我们现在考虑检测的问题是线性状态的聚合函数(聚合函数可以写成S = A(p)·S + B(p))。一般的解决这个问题是有挑战性的,因为聚合功能可以采取各种形式。 例如,任务是线性状态,但需要检测代数简化。

语言导向硬件设计

我们采取务实的做法,牺牲完整性仍然保留有用的函数。 具体来说,我们只通过匹配编译器的语法字段来查找线性状态的更新(即没有代数变换)。 尽管如此简化,Marple编译器正确识别所有的图7中的线性状态聚合。

为了描述线性状态检测的工作原理,我们介绍一下一些术语。 回想一下聚合函数需要两个参数(§2):状态变量列表(例如,计数器)和列表元组字段(例如,TCP序列号)。 我们用这个本小节中的术语变量来指状态变量或数组元素。 这些是唯一可以出现在聚合函数中的变量.

我们执行三步程序以实现线性状态检测,如图6。首先,对于聚合函数中的每个变量功能我们分配一个history。 这个history告诉我们以前有多少我们需要查看的数据包来准确地确定变量的值(history = 1表示当前数据包)。 例如,为了获得一个字节计数器的值,我们需要回头看看数据包流开始的地方(history =∞),而跟踪上一个TCP的变量序列号我们只需要回头看前一个数据包(history= 2)。 与history的定义一致,常数的history值为0,数组元素的变量被赋予history=1.对于状态变量,我们使用Alg。1来确定每个变量的history.

其次,每一个变量都有一个history,我们来看一下每个状态变量的history。如果s的history是有限数k,那么s取决于最后的k个数据包和该变量的状态更新是线性的,通过将A设置为0并将B设置为聚合函数本身。如果有无限的history,我们使用句法模式匹配以检查s的更新是否是线性状态.

第三,如果所有状态变量都具有线性状态更新,则聚合函数是线性的,我们生成辅助状态以合并聚合函数(§3)。如果不,我们使用Domino [56]中开发的一组有状态指令实现聚合功能。 我们现在详细描述三个步骤。

确定变量的history

为了了解alg. 1,观察状态变量的所有赋值是否只使用具有有限的history的变量,若是那么状态变量本身就有一个有限的history。 例如在图4中,在分配之后,lastseq具有history1,因为它只取决于当前数据包的字段tcpseq和payload_len。为了处理代码中的,如if(。。。){...}声明,我们对这一种情况进行概括。 状态变量在如下情况下有确定的history:如果(1)在其所有分支中具有有限的history,(2)每个条件语句中的变量具有有限history

具体来说,计算一个变量的history是依靠于他所在数据包流过位置的状态变量的history。 我们分别跟踪每个分支的history,即分支序列包含所有的状态。该算法以默认最大值作为每个状态变量(行)的history(即近似为∞)的初始量,并且重复执行定点计算(线3-20)迭代聚合函数中的语句(第7-16行).

对于聚合函数中的状态变量的每个赋值,该算法更新当前分支中状态变量的history。 对于聚合中的每个分支函数,该算法保存一个新的分支和当前分支的history(第10-14行)。 在每个迭代的最后,算法将每个变量的history递增表示变量又处理过一个数据包(第18行)。 算法为每个状态变量返回保守的history k,包括可能是max_bound(行1,Alg。1),以反映无限的history.

现在我们将通过聚合函数的语句函数GETHIST来说明history是如何进行维护的。考虑一个在分支语句ctx中的表达式x = expr,在分支上下文ctx中。 然后x的history是ctx中的history和表达式expr的history的最大值。 要确定expr的history,假设AST的expr包含变量x1,x2,...,xn作为其叶节点。 然后,expr的history是xi的history的最大值。 例如,在oos_count中赋值后,lastseq的history记录是最大值1(tcpseq和payload_len是当前数据包的函数)和0(对于封闭的最外层上下文为真).

确定状态变量的更新是否是线性状态

对于具有无限history的每个状态变量S,我们检查状态更新是否是线性的:(1)s的每次更新是语法上的, S←A·S + B中的A或B为0;(2)A,B和依赖于变量的每个分支语句具有有限的history。 这种方法是健全的,但不完整:它漏掉如的形式.

确定辅助状态

对于线性更新的每个状态变量,我们初始化一个新的四个辅助状态形成新插入的key:(1)运行的产品代号SA = 1; (2)数据包计数器c = 0; (3)入口日志,由插入后的钱k个数据包的数组元素决定; 和(4)退出日志,由最后k个包的字段决定。 在计数器的值变为k,当SA更新时,我们将A*SA赋给SA。当Key被驱逐时,我们将入口、出口日志中的SA保存到辅存中。

五、评估

我们以三种方式评估Marple。 在§5.1中,量化Marple查询所需的硬件资源。 在§5.2中,我们测量key-value存储的内存读取效率。 在§5.3和§5.4,我们展示了使用Marple编译成P4的两个案例研究Mininet上运行的行为模型:调试微爆[44]并计算流量大小分布.

5.1硬件计算资源

图7显示了几个Marple查询。对于每个查询,我们显示(1)是否所有的聚合都是线性状态的,(2)是否可以通过与后备存储正确合并来缩放,(3)所需的交换机资源,通过管道深度(级数),宽度(最大并行计数)和Banzai原子数(计算总数)来衡量。

图7显示了许多只包含线性状态聚合的有用的查询,并且大多数都扩展到大量的key(§3.2)。值得注意的是,流量大小统计和有损连接查询是由于它们包含emit(),因此尽管处于线性状态,但不可扩展。 在§3.4中,我们展示了如何以牺牲一定精度为代价重写其中一些查询(例如,有损连接).

我们通过编译到Banzai交换机管道模拟的查询来计算管道的深度和宽度。 Banzai是由无状态原子构成的,其对一对数据包数组字段和状态原子执行二进制运算(算术,逻辑和关系)。对于线性状态操作,我们使用乘法累加原子作为状态原子,而对于其他操作,我们使用Banzai自己的NestedIf原子[56]。 Domino编译器确定输入程序是否可以使用指定原子映射到管道。 如预期的那样,所有线性状态查询都可以通过累乘映射到管道上.

Marple查询所需的计算资源是适中的。 图7中的所有查询都需要一个短于11个阶段的管道。这是可行的,例如,RMT架构提供32个阶段[33]。此外,测量以外的功能可以并行运行因为每个阶段所需的原子数量最多为6个,可编程交换机每级提供100个并行指令(例如,RMT提供224 [33]).

5.2内存和带宽开销

在本节中,我们回答以下问题:

(1)片上key-value的尺寸是多少?

(2)失效率是多少?

(3)不可合并的查询的准确性

实验装置

我们对三种条件下的数据包进行一个Marple查询:来自10 Gbit/ s核心互联网路由器,一个来自2016年芝加哥(约150M包)[24]和来自2014年的圣荷西(约189M包)[23]; 和2.5小时2010年大学数据中心的流量(〜100M数据包)[32]。 我们将这些分别称为Core16,Core14和DC。

我们对一个五元组聚合的marple查询进行分析来评估内存大小对cache失效的影响。如§3.6所述,我们的硬件设计使用8路LRU缓存。 我们也评估另外两个几何形状:一个散列表,它将重复的key和完全关联的LRU删除。 比较我们的8路与其他硬件设计的LRU展示硬件复杂性和失效重取速度的关系.

逐出速率

每个被驱逐的键值对都流到辅存存储。 这需要后台存储能够以key-value被逐出的速率进行处理,这取决于数据包流入速率和逐出比例,即流入数据包的逐出比例。驱逐比取决于片上缓存结构,数据包流动路径和高速缓存大小(能存储的键值对数量)。 因此,我们在以下条件下衡量驱逐比(1)Core16轨迹的三种结构(图8b),(2)三个使用8路LRU结构(图8a)和(3)缓存的大小尺寸在216(65K)和221(2M)键值对之间。

图8b显示,由于LRU在开始逐出内容之前必须将cache存储空间填满,因此full lru具有最低的外传比例。 然而,8路关联缓存是一个很好的折中:它避免了在全相联LRU的硬件复杂度的同时,具有2%的失效比例。 图8a示出了DC情况下具有最低的驱逐比。这是因为它比其他两种情况有少得多的独立key-value,并且这些key不太可能被逐出。

驱逐比的倒数(作为一个分数)与缓存处理的key-value的数量是成正比的。 例如,对于Core14具有2^19key-value对缓存,服务器负载减少25×(相当于4%的驱逐比)。

驱逐率

撤销比率对交换机的特性来说是不可知的,如链路速度,链路利用率和片上高速缓存大小。将驱逐比转为驱逐速率(每秒驱逐),我们首先计算core16上平均数据包的大小(700字节)和链接利用率(30%).


接下来,我们评估64×10Gbit / s、64×100Gbit / s交换机上片上缓存大小。在64×10Gbit / s交换机上,SRAM密度≈3-4Mbit / mm2 [1],最小的交换机芯片占用了200mm2 [39]。因此,SRAM上64Mbit的缓存占用面积的10%左右,我们认为这是合理的。 对于最近的64×100Gbit / s交换机[5],SRAM密度是≈7Mbit/ mm2 [22],交换机占用≈500mm2, 256 Mbit缓存(7.3%区域开销)为合理的目标.

对于给定的查询,我们将这些缓存大小除以大小聚合的键值对来获取键值对的数量。然后,我们在图8中查找这个数字以获得该查询的驱逐比,我们使用率利用率和数据包大小将其转换为逐出速率.

一些抽样查询的排除率如图9所示。64×10-Gbit / s交换机,64Mbit高速缓存,我们观察到驱逐每秒≈1M包/s

。 对于64×100Gbit/ s的交换机一个256 Mbit的缓存和相同的平均数据包大小和利用率,驱逐速度可以达到每秒7.17M的数据包。 相对于每包收集器,Marple将服务器负


载降低25-80×

 

10 Gbit / s和100 Gbit / s交换机的驱逐速率都在每秒10M数据包/s以下,在多核向外扩展键值存储的能力之内[2,11,43],通常每个核心处理每秒100K-1M的操作。 例如,对于单核64×10Gbit / s交换机运行64位状态位的聚合,单个8核服务器足以处理驱逐速率为每秒1.08M数据包。 单个64×100Gbit / s交换机运行相同的聚合,驱逐率上升到5.18M数据包每秒,需要四个这样的服务器.

概括其他情景

图8也概括了不同状态大小的聚合。 第一,通过选择5元组的子集来粗化聚合key以减少驱逐率,因为工作中的实际使用的key较少。 我们认为5元组可能是最细粒度的并且仍然实用有用的汇总水平; 因此,我们的结果显示单个聚合的最坏的驱逐比例。二,在groupby值大小的变更导致不同的给定内存大小的键值对数不同。 三,运行具有相同数量键值对的多个groupby查询,并通过相同的key聚合,导致所有查询的同步逐出。 因此,驱逐速度可以从图8中读出相应地减少了内存大小.

不可合并查询的准确性

既不是查询线性状态或关联不能在后台存储中合并。如果这样一个查询的键被多次驱逐,Marple就不能保证其正确性并将其标记为无效。但是,这些钥匙的值仍然有效,如果它们永远不会被驱逐或者是驱逐一次,永远不会再出现。 我们量化查询的准确性作为在查询生命周期的有固定值的键的一部分。图10a显示了使用三种条件下的查询精度。DC结果几乎完美,因为它具有较少的唯一键。如果查询在较短的时间间隔内运行,则其准确性通常较高,因为缓存可能不是满的,而且key的很小一部分被逐出。 图10b显示了Core16下一个范围缓存大小下的效率与精度。 缩短查询从5分钟到1分钟提升精度10%.

5.3 案例研究#1:调试突发大流量数据包传输

为了展示Marple在实践中的应用,我们提出了一个案例研究,微波爆发,即由突发传输引起的延迟尖峰,数据包进入队列。我们在Mininet [47]中的设置包括四个主机(h1,h2,h3,h4)和两个交换机(S1,S2)。 交换机S1连接到


h1和h3,S2连接到h2和H4。 交换机通过单个链路连接并进行P4模型下的 [19] Marple编译的查询.

 

主机h2通过TCP从h1重复下载1MB的对象。同时,h3向h4突发发送UDP流量, h4进行确认。假设网络运营商注意到不规则的延迟尖峰下载(图11a)。 她怀疑交换机中的队列累积并通过写入来测量流量看到的队列深度:result=filter(pktstream,srcip == h1 anddstip == h2).

将结果写到在每个数据包上流到收集服务器。测到队列延迟后,她注意到队列大小的峰值在交换机上的出口端口3(图11b)与周期性匹配的延迟尖峰。 为了分离相应的流,她将流量分为“突发”,她定义为一系列数据包的间隔至少为800毫秒,这是有两个延迟尖峰决定的。 她发出以下Marple查询:

def burst_stats([last_time, nburst, time], [pkts, tin]):

if tin - last_time > 800000:

nbursts++;

emit();

else:

time = time + tin - last_time;

pkts = pkts + 1;

last_time = tin;

result = groupby(R1, 5tuple, burst_stats)

她运行查询72秒,并在图12中看到结果。她得出结论,正确地说,h3和h4之间的UDP流量导致了延迟尖峰。有18个UDP突发,平均数据包大小和持续时间与我们的仿真设置相匹配.


Marple的灵活性使得诊断变得简单。 相比之下,通过现有监测来定位微爆发的根本原因方法具有挑战性。

.

5.4 案例研究#2:Flowlet大小分布

我们在实践中演示了Marple的另一个用例:计算作为流动阈值的函数的流动尺寸分布,时间间隔高于其后续数据包被认为是在不同的流程。该分析具有许多实际用途,例如,用于配置基于流的负载均衡策略[27,60]。在特别是LetFlow [60]的表现在很大程度上取决于流量大小分布.

我们的设置使用Mininet与一个单一交换机连接五台主机:单个客户端和四个服务器。 流量大小从真实数据中心按照经验进行分发[28]。交换机运行图7中的“flowletsize histogram”查询delta的值,即流量阈值。

图13显示了各种值的流量大小的CD。 请注意,delta的实际值是该结果Mininet设置允许的带宽; 数据中心部署可能会使用较低的delta值.

六、相关工作

基于端点监控。 由于交换机支持有限的测量,许多系统使用终端来监控网络性能。 尽管终端解决方案是应用程序所必需的,对于调试所有网络问题是不够的,因为网络核心对于端点缺乏可见性。使用交换机增强端点解决方案例如INT,数据分散在多个端点上。 我们相信网络将需要基于终端的和基于交换机的系统,因为每个都看到别的看不到的东西。

基于交换机的监控

 大多数基于交换机的监控都具有每个流量计数(例如,NetFlow [7])和采样(例如,sFlow [21]),而不是性能测量。数据包采样可以因采样过疏而忽视重要事件[55]。 硬件NetFlow的实现不会保留每个流的记录,因为hash冲突的原因流查询表不能列表中插入新的流。marple缓存设计和合并解决了这个问题.

线速率数据包捕获设备,例如[10] 在高数据速率下记录所有数据包流量,为后期分析提供有价值的数据网络流量。理想情况下,性能监控应该可以在网络任何位置实现,但数据收集和存储高要求使得无法在任意位置运行数据包捕获。同样的问题限制了其他反映流量或收集的策略。 相比之下,Marple的灵活的语言和基于交换机的聚合处理开销很低,因为只收集需要的东西.

可编程交换机的计数器系统使用聚类数据结构和交换机上的流量计数器来进行流量统计。 Marple的性能监控能力远强于之前的系统。

带内网络遥测(INT)[12,44]显示队列长度和其他通过捎带开关的性能元数据他们在包上。 Marple使用类似INT的性能元数据,但是在交换机上直接提供灵活的聚合。 Marple的交换机上的聚合提供了相对于INT的三个优点。首先,没有聚合,每个INT端点需要处理高数据速率下的每个数据包。 二,交换机聚合保存每个数据包数据分发所需的带宽超过许多INT端点。 三,交换机聚合可以处理在它们到达端点之前丢弃INT数据包的情况。

网络查询语言 先前的网络查询语言[35,38,41,54]允许用户主要提出有关流量和计数统计的查询,因为他们的输入数据是使用收集的NetFlow和匹配动作规则计数器[51]。 相比之下,marple允许运营商通过设计 新的交换机硬件支持Marple查询,进而可以进行更多的查询。marple一些功能和关系结构与Gigascope [35]和Sonata [41]相似,但直接在交换机中支持聚合。marple允许运营商通过编程进行在线查询,从而实现以低开销收集细粒度的定制统计数据。它与离线查询系统相辅相成,离线查询通过分析采样或数据包捕获收集的历史数据来获得结果。

七结论

性能监测是维护任何大型系统的重要组成。 Marple的网络可见性和查询语言揭秘应用的网络性能,使运营商能够快速诊断问题我们希望网络运营商查询他们需要的任何东西,而不受限于有限的交换机功能集。Marple使细粒度,可编程网络监控向前迈进了一步,网络运营商而不是网络的厂商控制整个网络。

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值