文/翟恩南(恩南)
可编程交换芯片(programmable switching ASIC),是通过对交换机芯片的编程,将原本依赖CPU计算的软件版网络应用“卸载”到交换芯片硬件上,从而获得高达T级的网络数据包处理能力。自 2015年问世以来,可编程交换芯片就受到了业内云网络公司及科研学术界的极大关注与欢迎,利用可编程芯片来加速基础设施的处理能力,已被认为是未来一个重要的技术方向。
在当下现实环境中,可编程交换芯片极大地提升了网络应用的带宽并实现降低延迟,开发人员可以像软件开发一样灵活地定制交换芯片的处理逻辑。然而,鉴于当前的数据面编程仍处在采用底层的、芯片架构强绑定的(chip-specific)编程语言(例如P4)层面。因此,对于普通开发人员来说,开发数据面程序仍然是非常低效且复杂的任务(开发人员必须熟知不同芯片语言、硬件资源约束等细节,甚至要在截然不同的芯片之间进行分布式编程)。
作为可编程网络领域的早期发起者和推动者,阿里云基础设施网络团队自主研发了一套“面向可编程交换芯片的高级语言和编译器系统”——阿里云自研编译器,该系统可以做到跨芯片平台、跨语言平台、利用集中式的抽象进行分布式数据面编程,还能极大提升芯片资源的占用优化。相比于通过 P4 编写的数据面程序,该语言可以提升78%的编程开发效率,阿里云自研编译器可以降低最多84%的芯片资源占用。(本文已在SIGCOMM 2020发表)
No.1 可编程交换芯片的发展与现状
交换机扮演了计算机网络不可替代的重要角色,但传统交换机对于我们自行定义数据包处理极其不便。过去,交换机的数据包处理(packet processing)行为是出厂时就由厂商定义好的(例如:传统的交换机数据面有 ACL、IPv4 等,各自的厂商也会有一些自己独特的缺省默认行为),作为运维或开发人员,只能通过编写配置操作已实现好了的交换机数据面,并不能对交换机的数据面都有哪些表以及每个表能做什么样的包处理进行修改。用户如果希望对交换机的包处理能力进行定义,则必须联系厂商,由厂商为我们实现这些功能(这里有一个老生常谈的故事可以还原该现象:很早以前,使用者希望添加VxLAN的包处理能力,让博通在设备中添加该包头处理能力,这一简单的功能要求,居然耗时近 3 年才得以最终实现)。
具有革命性的一个节点终于到来了,2013 年,Barefoot公司希望改变上述现状,推出了P4可编程交换机——编程人员通过一个叫P4(Programming Protocol-Independent Packet Processors)的语言对Tofino芯片进行编程。P4交换机的出现,赋予了普通开发人员灵活地对交换机数据面包处理的定制化能力。换言之,作为开发人员和运维人员,能够自行修改数据面行为,而无需通过厂商。P4可编程交换机的出现,带来了三个重要的价值:
• 极大丰富了网络新能力、新特点的开发灵活性以及效率。
• 开发人员可通过可编程芯片直接把算法编程到交换机数据面中,使得这些被实现的算法拥有硬件级别的高吞吐和低延时性能。
• 由于数据面表实现就是开发人员自行指定的,整个交换机的行为,从过去的黑盒态转变为了白盒态。
目前,阿里云已部署多项基于可编程芯片的能力,如多应用超融合网关、网络可视化监控以及高带宽中心云网关等,都是基于 P4 开发的。同时,在网络学术界,基于可编程交换机的算法实现也层出不穷。由于可编程芯片的上述诸多优点以及受欢迎程度,当今各大交换机芯片生产商如博通、思科和Barefoot等纷纷推出了自己的可编程芯片(Barefoot已经推出了Tofino和Tofino2可编程芯片,可支持 P414 和 P416 编程语言;思科的Silicon One支持P416语言;博通即将推出Trident-4和Jericho-2可编程芯片支持NPL语言等),这一趋势决定了未来的云网络,特别是边缘云和数据中心网络等势必会配备大量可编程交换机,从而使得我们拥有灵活的对网络数据面行为具备可编程能力。
No.2 可编程网络大规模落地的关键挑战
虽然可编程交换芯片带来了很多益处,但“网络数据面编程”还处于较早期阶段(开发人员仍然需要使用low-level、chip-specific的编程语言,外加人工理解和处理许多芯片硬件资源和架构细节来开发所期待的数据面功能),这使我们想到了上世纪七八十年代的软件工程师,利用各种汇编语言在不同的CPU体系架构上(如ARM、X86等)开发程序的时代(试想一下,如果今天让一个程序员用汇编语言写一个Hadoop是一件多么痛苦且艰难的事情)。如何让网络开发人员能更方便、高效地进行网络数据面编程,已成为可编程网络大规模落地的关键问题,在具体实施上还面临着三大挑战:
挑战1:芯片架构和语言的多样性
关于这个挑战描述,我们可以用两个例子来阐释:
例1:当前的数据面编程严格绑定于可编程芯片的架构和支持语言,这使得开发人员必须熟练掌握多种编程语言(例如Tofino芯片和Silicon One编程只能使用P4,而对Trident-4编程只能用NPL,这使得开发人员必须熟练掌握上述多种编程语言)。
图1(如下)展示了实现 Flow filter 功能的 P414 和 NPL 代码:
通过图1的例子我们可以看到NPL和P4语言是非常不同的。然而更难的是,即使是支持相同的编程语言,如果芯片结构不同,写出来的程序也完全不同。例如,在 Tofino 芯片下我们用 P4 可以写出如下代码来比较 sip 是否等于 dip。
if (ipv4.sip == ipv4.dip) { // Do something }
而在Spectrum芯片上,因为芯片不支持32bits域的大小比较,我们无法直接写上述P4代码,只能拆成两个表:
(1) 第一个表计算 tmp = sip - dip;
(2) 第二个表判断 tmp 是否为 0 。
由此很容易发现:在不同芯片上就算用相同的编程语言开发,也会写出完全不同的代码,这就要求开发人员对每种芯片的体系结构和资源使用(包括寄存器大小、共享空间大小、pipeline 结构等)必须非常了解,否则就无法进行开发。
例2:想在Tofino芯片上实现一个移位取或操作的简单逻辑,则需要写出如图2 所示的这样拆成三张表实现的程序(“别看不同芯片看上去好像用的都是 P4,但是不同芯片结构下写出来的完全不是一种程序”已成为不少可编程开发人员的共识)
图2(如下):在 Tofino 芯片利用 P4 编写使用三张 table 完成一个简单的移位操作
action a_get_v16_1 () {
v16 = v8_a; }
action a_get_v16_2 () {
v16 = v16 << 8; }
action a_get_v16_3 () {
v16 = v16 | v8_b; }
table get_v16_1 {a_get_v16_1};
table get_v16_2 {a_get_v16_2};
table get_v16_3 {a_get_v16_3};
control get_v16{
apply(get_v16_1);
apply(get_v16_2);
apply(get_v16_3);
}
挑战2:分布式编程
已有芯片语言存在的另一个重大问题是:只能进行单设备编程、底层芯片语言对于一个网络编程的不成熟度普遍存在。一旦有数据面程序分布在多台设备上,而这些设备又是由不同芯片、不同语言所组成,那么使用已有的底层芯片语言开发起来将会异常困难。如下图3所示:
图3(如下):在ToR3、ToR4、Agg3和Agg4上部署一个load balancer
图3的例子中,左侧展示了一个数据中心网络,该网络ToR(Top-of-Rack)层部署了Tofino芯片和Silicon One芯片的交换机,为了保证靠近server端的强可编程性;在Agg层部署了Trident-4芯片的交换机;在Core层部署了不可编程的Tomahawk芯片的交换机,为了保证高带宽。
现在假设有一个需求,要在ToR3、ToR4、Agg3和Agg4 上部署一个Load Balancer(LB),这就造成了挑战。因为两层设备的芯片和语言都非常不同:在Silicon One芯片上写满足Silicon One芯片架构约束的P4程序、在Tofino芯片上写满足Tofino芯片架构约束的P4程序、在Trident-4芯片上写NPL程序;同时,又要考虑在不同设备上程序的功能不同。LB程序包含两个表:ConnTable和VIPTable。
究竟怎么实现:是在Agg上实现ConnTable,在ToR上实现VIPTable?还是都在Agg上实现?还是在Agg实现一部分ConnTable,而在ToR上实现另外一部分ConnTable和全部的VIPTable?大家可以从图3右上角看到这些选择,而这些选择都是要基于对不同芯片的资源大小的评估,和更多具体情况的考量。如果开发人员不需要关心这些底层细节,只写程序,而由编译器完成这些底层优化和分配的任务该多好。
挑战3:芯片资源优化问题
在上述例子中,除了要部署一个LB功能外,我们还希望部署一个全网范围的INT(In-band Network Telemetry)能力,以及仅在ToR层部署Firewall能力,那么就会得到如图4所示的一个网络。
图4:同时部署三个程序INT、Load Balancer和Firewall到数据中心网络
在图4的例子中,可以看到设备ToR 3和ToR 4要同时部署三个功能(LB、INT和Firewall)。因为每个可编程芯片都是有严格资源和空间限制的,编写程序占用资源的不同很可能导致这三个程序无法被同时编写到同一个芯片上(当然,如果程序写的足够优化,是可以同时写到同一个芯片上的!那么这里又到了考验我们开发人员能力的时候:无时无刻不需要通过人工方式,来保证程序占用资源的优化),这提升了数据面程序的复杂性。
小结
综上所述,当前的底层芯片编程能力还处于一个较早阶段,对于上面提到的三个挑战(芯片架构语言多样性、分布式编程、芯片的资源优化问题),都需要开发人员花费大量时间和精力来以人工的方式解决上述问题。这种情况让我们联想起上世纪七十年代,软件开发人员使用不同CPU体系结构的汇编语言编程的不方便性,从而导致了C语言作为一款易用、优化资源、跨平台的高级语言的诞生。
No.3 作为解决方案的“阿里云自研编译器”
针对上述行业内的三大挑战,阿里云基础设施网络团队设计并开发了由阿里云完全拥有自主知识产品的全球第一个面向可编程网络的高级程序语言编译器——阿里云自研编译器。如图5所示,开发人员只需要利用阿里云自研编译器提供的高级语言编写自己期待的数据面“程序逻辑”,编译器就可以帮忙自动生成符合不同芯片资源的程序,并下发优化后的程序到对应的交换机上。换言之,开发人员只需要考虑功能性的实现,而不再需要关心具体的芯片资源、如何放置、以及具体底层语言和架构限制等问题。
图5:阿里云自研编译器体系结构
为了解决第一个挑战(芯片架构语言多样性),提供了一套高级语言,该语言提供了屏蔽底层硬件细节的更高维抽象(abstraction)。做个类比,就好比在汇编语言年代,我们为变量和程序分配内存,需要写上百行的汇编代码,而用C语言我们只需要用malloc就可以完成是一个道理。在阿里云自研编译器提供的语言中,我们不需要考虑NPL或P4不同版本的区别,只需要用一套语言就可以完成。回到前面图2的例子中。利用 P4 我们需要写如图2所示的代码,但是用阿里云自研编译器,开发人员只需要写出下面这一行代码:
v16 = (v8_a << 8) | v8_b
这一行语句,极大减少了开发人员的编程时间和精力。
为了解决第二个挑战(分布式编程),阿里云自研编译器可以自动读取网络拓扑以及允许开发人员制定不同功能的部署范围,无需指出具体要部署到哪些设备上,或如何在不同设备上编写各自的芯片程序。阿里云自研编译器会按照制定网络范围,以及网络当前的 context信息,自动为各程序分配部署方案,完全无需人工干涉。
为了解决第三个挑战(芯片资源优化问题),阿里云自研编译器实现了大量 synthesis(代码合成)和代码优化算法,建立起来的编译器后端系统可以充分压缩和优化所生成代码使用的表数目以及对芯片硬件资源的占用程度,从而极大节省了使用的芯片资源和空间。
阿里云基础设施网络团队利用阿里云自研编译器语言和编译器已重写了很多真实世界的可编程芯片程序(例如:INT、NetChain、NetCache、Switch等),通过比较发现,使用阿里云自研编译器生成的P4代码和原人工手写的P4代码有着显著差异:用阿里云自研编译器高级语言编写数据面程序,不仅效率上提升了78%,而且生成的芯片代码占用的资源也大大降低了,最多可节省84%的芯片资源占用。
推荐阅读:剑出鞘威震江湖 阿里云在SIGCOMM'20 拿下大四喜!
No.4 阿里云自研编译器的价值和意义
阿里云自研编译器,是阿里云自主研发的面向跨平台可编程交换芯片编程的编译系统,该编译系统是一个具有实际意义的技术先进性成果,同时运用了大量语言设计、编译器开发等深度技术。
在学术层面,鉴于编译器的技术领先性、创新性以及实用性,由阿里云基础设施网络研发事业部网络研究团队撰写的相关论文,已被国际排名第一的网络学术会议ACM SIGCOMM 2020接收,还在会上与线下被积极讨论,反响甚佳。
推荐阅读:再续前缘 阿里云基础设施网络团队ACM SIGCOMM 2021续创佳绩
在实际应用层面,阿里云自研编译器已在阿里云的日常可编程数据面开发流程中被广泛使用,在边缘云和中心云的网关开发等场景中,极大提升了开发人员的编程效能。
面向未来,阿里云自研编译器的开发人员也在进一步完善和优化编译器的能力,正从可证明最优的资源利用、基于程序依赖关系的资源拆分,以及程序编译排查等方向对阿里云自研编译器进行迭代性研发改进。
我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。