队列调度是计算机网络中的一个核心问题,过去的几十年里,在工业网络、数据中心网络、广域网等场景中,大量的调度算法被设计来提供不同的特性和优化不同的目标,可编程包调度更是近几年数据中心网络研究领域的皇冠。随着应用对网络服务质量的要求不断提高,确定性网络中的队列机制也不断推陈出新。本文带大家玩转队列,按照队列的概念、队列的演进、队列的确定性增强,分为三小节,揭开众多机制背后的核心奥秘。
本文首先解释队列的概念,从队列的位置讲起,介绍单个队列的入队、调度、出队过程,呈现先入先出(FIFO, First-In-First-Out)、压入先出(PIFO,Push-In-First-Out)两种经典的队列形式,以及相关的调度算法;然后分析队列机制的演进过程,从单队列延伸到多队列,从硬件队列延伸到软件队列,以及每包、每流、每类、每队列等调度粒度;最后讲解确定性网络中的流量整形、门控、帧抢占等队列增强机制。
队列的概念
什么是队列?
如图1所示,一台交换机有多个端口,交换机内部通过交换矩阵将端口连接起来,数据包总是从一个端口进,然后通过交换矩阵,再从另一个端口出,在全双工模式下每个端口既是入端口也是出端口。交换机里有一块存储资源叫缓冲区,大部分交换机的片上缓存都不大,一般是几MB到几十MB,均分到各个端口也就几百KB,有突发流量时大概能存下几十个包。
虽然单端口带宽在不到十年的时间里从1G发展到了400G,但缓存并没有很大提升,因为大缓存虽能减少丢包,但需要相对多的寻址时间,会降低数据包的转发速度,增加设备成本和网络延迟。队列是缓冲区里的一种数据结构,用于针对不同的应用优化网络的丢包率和时延性能。
为什么需要队列
队列主要解决排队的问题,如果网络是一个带宽相同的线型拓扑,速率处处匹配,流量总大小不超过端口带宽,则无需队列。如图2所示,现实中网络拓扑复杂,存在流量的微突发(比如由TCP滑动窗口导致的突发)和多打一等上下游端口速率不匹配问题,因此需要队列调度。
在缓存排队的过程中,有的应用需要低时延,要求缓存小且被优先调度;有的应用需要零丢包,则缓存越大越好;有的追求网络吞吐量和利用率,要求针对带宽进行优化;有的追求公平性,要求队列资源尽量平均分配;有的又对流完成时间有要求,需要降低流量的长尾时延。同时满足多种应用需求给队列调度带来了巨大的挑战。
队列的位置
关于缓冲队列应该放在什么位置,有很多不同的见解,如图3所示,总的有输出端口缓冲、输入端口缓冲+虚拟输出队列(Virtual Output Queues, VOQs)、交叉点缓冲、共享缓冲四种方式[1]。
- 输出端口缓冲是将缓冲区放在出端口的位置,它相比于输入端口缓冲有更好的时延和吞吐性能,然而它要求每个出端口具有N倍的线速处理能力,以处理N个入端口连向同一个出端口的情况,这种N倍的处理能力往往是不切实际的;
- 于是有了入端口缓冲+虚拟输出队列VOQs的架构,可以满足线速处理但吞吐量下降,这也是当前最常用的缓冲方式。
- 交叉点缓冲能够保证高吞吐,但对于具有N 个入端口和N个出端口的交换机需要 O(N*N) 的内存成本。事实上,在这种架构中每个输入-输出对都拥有自己的交叉点缓冲,不能共享不同的交叉点缓冲区,因此当N较大时,会造成相当大的内存浪费。在片上内存有限的嵌入式系统中,高昂的内存成本也是不可接受的。
- 最后还有一种“交换-内存-交换”的共享缓冲架构,在架构中间使用至少2N-1个缓冲区来模拟输出缓冲区切换;这些缓冲区由所有端口共享,因此即使仅使用一小部分端口,也可以获得高达 100% 的内存使用率。
总的来说,不管缓冲区放在什么位置,当提到“队列调度”时,默认发生在交换机出端口就好了。
单队列调度:
- 下面来看单队列的调度,其分为入队、调度、出队三个过程。其中,入队是做接入控制,对流量进行识别和分类,对不符合要求的流量进行丢弃;调度是对队列中的数据包进行选择出队,在单队列中有“先入先出FIFO”、“压入先出PIFO”两种最常用的模式,在模式之上可以做各式各样的调度算法创新;出队是将数据包传输到链路上,链路连接到下一节点的入端口,在出队时做流量整形(即控制流量在出端口传输时的发送速率),可以降低或避免下游节点拥塞。
先入先出队列:
- 先入先出队列是最基本最纯粹的队列,是其他队列改进的母版。如图4(a)所示,它让最先到达的数据包最先出队,不改变包的顺序,也就是没有调度算法,不进行调度。当然,它也可以在入队做接入控制,在出队做流量整形。
压入先出队列:
- 如图4(b)所示,每个数据包携带有一个数字标记作为它的秩,一般秩越小表示数据包的等级越高,越需要被优先传输[2]。压入先出队列首先在入队时对不符合秩条件的数据包进行丢弃,然后根据秩大小进行排序,把秩小的包压入队头优先出队传输。
队列调度算法:
- 仅在单队列模式的基础上,人们就做了大量的队列调度算法创新,比如主动队列管理,给队列长度设定一个阈值,如果队列长度超过这个阈值,就丢弃后面的包,从而保证传输时延不会过大;再比如在此基础上衍生出的ECN显示拥塞通知,如果队列长度超过这个阈值,就通知上游发送节点降低发送速率,直到反馈到发送端降低发送速率,等到队列长度小于这个阈值了,又可以通知发送端增大发送速率。
此外,在选择谁应该被放在前面优先调度上,不仅可以用秩,还可以用时延预估,比如截止时间小的包优先调度,可以用流的体积,当大象流和老鼠流同时到达,体积小的老鼠流优先调度。在确定性网络中,还可以给队列长度设定一个上界,流量不超过队列长度的最大值,从而保证零丢包以及有界低时延。
下一节将介绍队列机制的演进过程,以及确定性网络中的队列增强机制,更多内容请看下回分解。
参考文献:
[1] Z. Li et al., "Time-triggered switch-memory-switch architecture for time-sensitive networking switches," in IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems, vol. 39, no. 1, pp. 185-198, 2020.
[2] Zhuolong Yu, et al., “Programmable packet scheduling with a single queue”. In SIGCOMM '21. New York, USA, 179–193.
作者简介:黄玉栋,北京邮电大学网络与交换国家重点实验室博一在读,研究方向为未来网络体系架构,确定性网络,邮箱地址: hyduni@163.com。