BBR 拥塞控制草案

BBR 拥塞控制

原文:draft-cardwell-iccrg-bbr-congestion-control-02 (ietf.org)

抽象

本文档指定了 BBR 拥塞控制算法。BBR(“瓶颈带宽和往返传播时间”)使用传输连接的传输速率、往返时间和数据包丢失率的最新测量值来构建网络路径的显式模型。然后,BBR 使用此模型来控制它发送数据的速度以及它在任何时候允许在网络中飞行的最大数据量。相对于基于损失的拥塞控制算法,如 Reno [RFC5681] 或 CUBIC [RFC8312],BBR 为缓冲区或随机损失较浅的瓶颈提供了更高的吞吐量,并为缓冲区较深的瓶颈提供了大大降低的排队延迟(避免了“缓冲区膨胀”)。BBR 可以在任何支持数据包传递确认的传输协议中实现。到目前为止,开源实现可用于 TCP [RFC793] 和 QUIC [RFC9000]。本文档指定了 BBR 算法的版本 2,有时也称为 BBRv2 或 bbr2。

此备忘录的状态

本互联网草案的提交完全符合《公约》的规定。 BCP 78 和 BCP 79 的规定。

互联网草案是互联网工程任务的工作文件 力 (IETF)。请注意,其他组也可能分发工作 作为互联网草稿的文件。当前互联网草案的列表是 在 Active Internet-Drafts.

互联网草稿是有效期最长为六个月的草稿文件 并可能在任何时候被其他文档更新、替换或废弃 时间。使用互联网草案作为参考是不合适的 材料或引用它们,而不是作为“正在进行的工作”。

本互联网草案将于 2022 年 9 月 8 日到期。

目录

1. 引言

互联网传统上使用基于损失的拥塞控制算法,如 Reno ([Jac88], [Jac90], [WS95] [RFC5681]) 和 CUBIC ([HRX08], [RFC8312])。这些算法多年来一直运行良好,因为它们与互联网路径中普遍存在的带宽延迟积和缓冲程度足够匹配。随着互联网的发展,基于损失的拥塞控制在几个重要场景中越来越成问题:

  1. 浅缓冲区:在浅缓冲区中,即使链路利用率低,也可能发生数据包丢失。对于使用具有浅缓冲区的商用交换机的高速、长距离链路,基于损失的拥塞控制可能会导致吞吐量严重不足,因为它会反应过度,在数据包丢失时成倍降低发送速率,此后只会缓慢增加其发送速率。即使数据包丢失是由链路大部分空闲时的瞬态流量突发引起的,也会发生这种情况。
  2. 深度缓冲区:在当今互联网的边缘,基于损失的拥塞控制可能会导致“缓冲区膨胀”的问题,通过在最后一英里链路中反复填充深度缓冲区并导致高排队延迟。
  3. 动态流量工作负载:对于任何深度的缓冲区,新进入的流的动态混合或最近空闲的流的数据传输都可能导致频繁的数据包丢失。在这种情况下,基于损失的拥塞控制可能无法保持其公平的带宽份额,从而导致应用程序性能不佳。

在上述浅缓冲区 (1.) 或动态流量 (3.) 场景中,在实践中,基于损失的拥塞控制很难实现全吞吐量:对于 CUBIC,在 100 毫秒 RTT 内维持 10Gbps 需要低于 0.000003% 的丢包率(即丢包间隔超过 40 秒),而在 100 毫秒的 RTT 路径上,更可行的丢失率(如 1%)最多只能维持 3 Mbps [RFC8312]。无论瓶颈链接能够做什么,或者连接的公平份额是多少,这些限制都适用。此外,如果未能达到公平的份额,可能会导致吞吐量不佳,并且对于延迟敏感的应用程序来说,尾部延迟很差。

BBR(“瓶颈带宽和往返传播时间”)拥塞控制算法是一种基于模型的算法,它采用的方法与基于损失的拥塞控制不同:BBR 使用传输连接的传输速率、往返时间和数据包丢失率的最新测量值来构建网络路径的显式模型,包括其估计的可用带宽、带宽延迟乘积、 以及连接可以在不造成过多队列压力的情况下在网络中放置的最大数据量。然后,它使用此模型来指导其寻求高吞吐量和低队列压力的控制行为。

本文档介绍BBR算法的当前版本BBRv2。该算法的先前版本BBRv1之前在高层次[CCGHJ16][CCGHJ17]中进行了描述。BBR在允许高利用具有浅缓冲区的高速网络方面的意义已在其他工作[MM19]中进行了讨论。BBR算法的积极工作仍在继续。

本文档的组织结构如下。第 2 节提供了将在本文档中使用的各种定义。第三节概述了BBR算法的设计,第四节详细介绍了BBR算法,包括BBR的网络路径模型、控制参数和状态机。第 5 节介绍了实施状态,第 6 节介绍了安全注意事项,第 7 节指出没有 IANA 注意事项,第 8 节以致谢结束。

2. 术语

本文档定义了 BBR 算法的状态变量和常量。

以下未定义的以 C、P 或 rs 开头的变量在 [draft-cheng-iccrg-delivery-rate-estimation] 中定义。

2.1. 传输连接状态

C.已交付:在传输连接 C 的生命周期内迄今为止交付的数据总量(以八位字节或数据包为单位跟踪)。

SMSS:发送方最大段大小。

is_cwnd_limited:如果连接在最后一个数据包定时往返行程中的任何一点都充分利用了其 cwnd,则为 True。

InitialCwnd:传输协议实现在初始化时为连接设置的初始拥塞窗口。

2.2. 每个数据包的状态

packet.delivered:C.当给定的数据包通过传输连接发送时已交付 C.

packet.departure_time:给定数据包的最早起搏出发时间。

packet.tx_in_flight:在传输数据包时估计正在传输的数据量。

2.3. 每 ACK 速率采样状态

rs.delivered:从刚刚确认的数据包传输到当前时间之间传输的数据量。

rs.delivery_rate:从刚刚确认的数据包中获取的传递速率(又称带宽)样本。

rs.rtt:根据刚刚确认的段中最近发送的段计算的 RTT 样本。

rs.newly_acked:在刚刚收到的 ACK 上累积或选择性确认的数据量。(此数量在 [RFC6937] 中称为“DeliveredData”。

rs.newly_lost:刚收到的 ACK 上新标记为丢失的数据量。

rs.tx_in_flight:在传输刚刚被确认的数据包时估计正在传输的数据量(刚刚收到的 ACK 确认的段中最近发送的段)。

rs.lost:在传输和确认刚刚被确认的数据包之间被宣布丢失的数据量(刚刚收到的 ACK 确认的段中最近发送的段)。

2.4. 输出控制参数

cwnd:传输发送方的拥塞窗口,用于限制传输中的数据量。

BBR.pacing_rate:BBR 流的当前步调速率,用于控制数据包间距。

BBR.send_quantum:一起计划和传输的数据聚合的最大大小。

2.5. 起搏状态和参数

BBR.pacing_gain:用于缩放 BBR.bw 以产生BBR.pacing_rate的动态增益因子。

BBRPacingMarginPercent:用于缩放 BBR.bw 以产生 BBR.pacing_rate 的静态折扣因子 1%。

BBR.next_departure_time:下一个数据包 BBR 计划传输的最早起搏出发时间。

2.6. cwnd状态和参数

BBR.cwnd_gain:用于缩放估计的 BDP 以产生拥塞窗口 (cwnd) 的动态增益因子。

BBRStartupPacingGain:一个常数,指定用于计算起搏率的最小增益值,该值将允许发送率每轮加倍 (4*ln(2) ~= 2.77) [BBRStartupPacingGain];在启动模式下用于 BBR.pacing_gain。

BBRStartupCwndGain:一个常数,指定用于计算 cwnd 的最小增益值,该值将允许发送率每轮加倍 (2.0);在启动模式下用于 BBR.cwnd_gain。

BBR.packet_conservation:一个布尔值,指示 BBR 当前是否正在使用数据包保护动态来绑定 cwnd。

2.7. 通用算法状态

BBR.state:BBR 状态机中 BBR 流的当前状态。

BBR.round_count:到目前为止经过的数据包定时往返次数。

BBR.round_start:BBR 在推进BBR.round_count的 ACK 上每次数据包定时往返一次设置为 true 的布尔值。

BBR.next_round_delivered:packet.delivered 值,表示数据包定时往返的结束。

BBR.idle_restart:当且仅当连接在空闲后重新启动时为真值的布尔值。

2.8. 核心算法设计参数

BBRLossThresh:探测带宽时允许的最大每次往返数据包丢失率(默认值为 2%)。

BBRBeta:在连接检测到数据包丢失的每次往返行程中进行的默认乘法减值(值为 0.7)。

BBRHeadroom:当尝试在路径中留出可供交叉流量使用的空闲空间(例如,瓶颈缓冲区中的空闲空间或瓶颈链路中的空闲时隙)时,应用于BBR.inflight_hi的乘法因子(值为 0.85)。

BBRMinPipeCwnd:BBR 目标的最小 cwnd 值,以允许与遵循“每隔一个数据包 ACK”延迟 ACK 策略的 TCP 端点进行流水线:4 * SMSS。

2.9. 网络路径模型参数

2.9.1. 数据速率网络路径模型参数

数据速率模型参数共同估计达到流可用全部带宽所需的发送速率 (BBR.max_bw) 以及与队列压力目标 (BBR.bw) 一致的最大起搏速率控制参数。

BBR.max_bw:使用 BBR 传送率采样算法 [draft-cheng-iccrg-delivery-rate-estimate] 获得的窗口化最近带宽样本 - 在当前或上一个带宽探测周期内(或在启动期间,如果流仍处于该状态)期间测量。(长期模型的一部分。

BBR.bw_hi:算法根据当前或上一个带宽探测周期中的信号(以损耗衡量)估计将产生可接受的队列压力的长期最大发送带宽。(长期模型的一部分。

BBR.bw_lo:根据当前带宽探测周期中的任何损失信号,该算法估计的短期最大发送带宽与当前网络路径传输速率匹配是安全的。这通常低于 max_bw 或 bw_hi(因此得名)。(短期模型的一部分。

BBR.bw:在给定模型中的所有可用信号的情况下,算法估计的最大发送带宽适用于匹配当前网络路径传输速率,在任何时间尺度上。它是 max_bw、bw_hi 和 bw_lo 的 min()。

2.9.2. 数据量网络路径模型参数

数据量模型参数共同估计达到流可用全部带宽所需的飞行中数据量 (BBR.max_inflight),以及与队列压力目标 (cwnd) 一致的最大数据量。

BBR.min_rtt:在最后一个 MinRTTFilterLen 期间测量的窗口最小往返时间样本 = 10 秒。这尝试在共享瓶颈的所有连接都使用 BBR 时估计网络路径的双向传播延迟,但也允许 BBR 估计 bdp 估计值,如果存在共享瓶颈的传统基于损失的 Reno 或 CUBIC 流,则允许完全吞吐量。

BBR.bdp:网络路径的 BDP(带宽-延迟乘积)的估计值,计算公式为:BBR.bdp = BBR.bw * BBR.min_rtt。

BBR.extra_acked:对网络路径中最近聚集程度的估计数据量。

BBR.offload_budget:使用发送方 (TSO/GSO) 和接收方(LRO、GRO)主机卸载机制时实现完全吞吐量所需的最小数据量的估计值。

BBR.max_inflight:根据 BDP 估计值 (BBR.bdp)、聚合估计值 (BBR.extra_acked)、卸载预算 (BBR.offload_budget) 和 BBRMinPipeCwnd,估计充分利用流可用的瓶颈带宽所需的飞行中数据量。

BBR.inflight_hi:与BBR.bw_hi类似,该算法根据当前或上一个带宽探测周期中的信号(通过损失衡量)估计的飞行中数据的长期最大容量将产生可接受的队列压力。也就是说,如果流正在探测带宽,并观察到发送特定数量的动态数据会导致损失率高于损失率目标,则它会为该数据量设置inflight_hi。(长期模型的一部分。

BBR.inflight_lo:与BBR.bw_lo类似,该算法根据当前带宽探测周期中的任何损失信号,估计的飞行中数据的短期最大容量对于匹配当前网络路径传递过程是安全的。这通常低于 max_inflight 或 inflight_hi(因此得名)。(短期模型的一部分。

2.10. 应对拥堵的状态

BBR.bw_latest:传输带宽 (rs.delivery_rate) 的最大往返 1 次。

BBR.inflight_latest:已传输数据量 (rs.delivered) 的 1 次往返最大值。

2.11. 估计 BBR.max_bw

BBR的。MaxBwFilter:用于跟踪最大最近rs.delivery_rate样本的筛选器,用于估计 BBR.max_bw。

MaxBwFilterLen:BBR 的筛选器窗口长度。MaxBwFilter = 2 (表示最多 2 个 ProbeBW 周期、当前周期和前一个完整周期) 。

BBR.cycle_count:BBR.max_bw 过滤器窗口使用的虚拟时间。请注意,BBR.cycle_count只需要用一个比特进行跟踪,因为 BBR.MaxBwFilter 只需要跟踪两个时间段的样本:上一个 ProbeBW 周期和当前 ProbeBW 周期。

2.12. 估算BBR.extra_acked

BBR.extra_acked_interval_start:估计由于聚合效应而确认的超额数据量的时间间隔的开始。

BBR.extra_acked_delivered:自 BBR.extra_acked_interval_start 年以来标记为已送达的数据量。

BBR的。ExtraACKedFilter:跟踪路径中最近最大聚合程度的最大过滤器。

BBRExtraAckedFilterLen = BBR 的窗口长度。ExtraACKedFilter 最大滤波器窗口:10(以数据包定时往返为单位)。

2.13. 启动参数和状态

BBR.filled_pipe:一个布尔值,记录 BBR 是否估计它曾经充分利用过其可用带宽(“填满管道”)。

BBR.full_bw:最近的基线 BBR.max_bw,用于估计 BBR 是否在 Startup 中“填满了管道”。

BBR.full_bw_count:非应用限制的往返次数,BBR.full_bw没有大幅增加。

2.14. ProbeRTT和min_rtt参数和状态

2.14.1. 估计BBR.min_rtt的参数

BBR.min_rtt_stamp:获得当前BBR.min_rtt样本的挂钟时间。

MinRTTFilterLen:指定 BBR.min_rtt 分钟过滤器窗口长度的常量,MinRTTFilterLen 为 10 秒。

2.14.2. 调度ProbeRTT的参数

BBRProbeRTTCwndGain = 一个常数,指定在 ProbeRTT 期间计算 cwnd 的增益值:0.5(意味着 ProbeRTT 尝试将飞行中数据减少到估计 BDP 的 50%)。

ProbeRTTDuration:一个常量,指定 ProbeRTT 状态在飞行到 BBRMinPipeCwnd 或更少数据包时保持的最短持续时间:200 毫秒。

ProbeRTTInterval:指定 ProbeRTT 状态之间的最小时间间隔的常量:5 秒。

BBR.probe_rtt_min_delay:上次 ProbeRTTInterval 中记录的最小 RTT 样本。

BBR.probe_rtt_min_stamp:获得当前BBR.probe_rtt_min_delay样本的挂钟时间。

BBR.probe_rtt_expired:一个布尔值,记录BBR.probe_rtt_min_delay是否已过期,以及是否应在应用程序空闲期或过渡到 ProbeRTT 状态的情况下进行刷新。

本文件中的关键字“必须”、“不得”、“必需”、“应”、“不应”、“应该”、“不应”、“推荐”、“可以”和“可选”应按照 [RFC2119] 中的描述进行解释。

3. 设计概述

3.1. 高级设计目标

BBR 的高级目标是实现以下两个目标:

  1. 流可用的全部吞吐量(或其中的近似公平份额)

    • 以快速且可扩展的方式实现(在 O(log(BDP)) 时间内使用带宽)。
    • 平均丢包率高达 1%。
  2. 低队列压力(低排队延迟和低数据包丢失)。

这些目标是相互矛盾的:发送速度更快可以提高实现 (1) 的几率,但会降低实现 (2) 的几率,而发送速度较慢可以提高实现 (2) 的几率,但会降低实现 (1) 的几率。因此,该算法无法独立地最大化吞吐量或最小化队列压力,必须共同优化两者。

为了尝试实现这些目标,并寻求一个高吞吐量和低延迟的工作点 [K79] [GK81],BBR 旨在从两个维度调整其发送过程以匹配网络交付过程:

  1. 数据速率:理想情况发送数据的速率应与网络传输流数据的速率(可用的瓶颈带宽)相匹配
  2. 数据量:网络中传输的未确认数据量理想情况下应与路径的带宽延迟乘积 (BDP) 匹配

数据速率(通过起搏率)和数据量(直接通过拥塞窗口或cwnd;以及间接通过起搏率)的控制都很重要。任一维度的不匹配都可能导致发送方无法满足其高级设计目标:

  1. 容量不匹配:如果发送方的发送速率与可用带宽完全匹配,但其传输中的数据量超过了 BDP,则发送方可能会保持较大的站立队列,从而增加网络延迟并面临数据包丢失的风险。
  2. 速率不匹配:如果发送方的飞行中数据量与 BDP 完全匹配,但其发送速率超过了可用的瓶颈带宽(例如,发送方以发送方的链接速率以不同步的方式传输 BDP 数据),则多达 BDP 的数据可能会突入瓶颈队列,从而导致高延迟和/或高损失。

3.2. 算法概述

基于上述基本原理,BBR 尝试将大部分时间用于将其发送过程(数据速率和数据量)与网络路径的交付过程相匹配。为此,它探索了 (1) 数据速率(“带宽”或“吞吐量”)和 (2) 数据量(“飞行中数据”)的二维控制参数空间,目的是找到每个控制参数的最大值,这些值与其队列压力目标一致。

根据给定网络路径在给定时间出现的信号,队列压力的目标根据以下最严格的标准来衡量:

  • 估计在瓶颈缓冲区中排队的数据量 (data_in_flight - estimated_BDP):目标是将此数量保持在 1.5 * estimated_BDP 或以下
  • 丢包率:目标是最大每次往返丢包率为 BBRLossThresh=2%(平均丢包率要低得多)

3.3. 状态机概述

BBR 使用简单的状态机改变其控制参数,该状态机旨在实现高吞吐量、低延迟和大致公平的带宽共享,同时保持网络路径的最新模型。

BBR 流以 Startup 状态开始,并快速提高其发送速率,以快速估计最大可用带宽 (BBR.max_bw)。当它估计瓶颈带宽已得到充分利用时,它会进入 Drain 状态以耗尽估计的队列。在稳定状态下,BBR 流主要使用 ProbeBW 状态,定期短暂地向探测发送较快的探测以获得更高的容量,然后短暂地发送较慢的探测以尝试耗尽任何生成的队列。如果需要,它会短暂进入 ProbeRTT 状态,以降低对较低BBR.min_rtt样本的探测发送速率。下面将介绍每种状态的详细行为。

3.4. 网络路径模型概述

3.4.1. 网络路径模型的高级设计目标

概括地说,BBR 模型试图反映网络路径的两个方面:

  • 对实现最大吞吐量所需的条件进行建模:估计充分利用流可用的瓶颈带宽的公平份额所需的最小数据速率和数据量。这必须包含对最大可用带宽 (BBR.max_bw)、路径的 BDP (BBR.bdp) 的估计值,以及对终端主机或网络路径上产生聚合效果的机制的任何卸载功能的要求 (总计为 BBR.max_inflight) 。
  • 对实现低队列压力的允许条件进行建模:估计与队列压力目标一致的最大数据速率 (BBR.bw) 和数据量 (cwnd),通过估计的排队和数据包丢失程度来衡量。

请注意,这两个方面是相互矛盾的:当流尽可能快地发送并占用尽可能多的瓶颈缓冲区槽时,流可以使用最高的吞吐量;当流量发送尽可能慢并占用尽可能少的瓶颈缓冲槽时,流量可以实现最低的 QUE 压力。为了解决紧张关系,该算法旨在实现可实现的最大吞吐量,同时仍满足队列压力目标。

3.4.2. 网络模型的时间尺度

在较高层次上,BBR 模型试图在两个不同的时间尺度上反映网络路径的属性:

3.4.2.1. 长期模型

一个目标是让 BBR 在很长一段时间内保持可用带宽公平份额的高平均利用率。这需要对路径的数据速率和体积容量进行估计,这些估计值在很长的时间间隔内是稳健的。这意味着对拥塞信号具有鲁棒性,这些拥塞信号可能是嘈杂的,或者可能反映出在 ACK 到达时已经减弱的短期拥塞。这也意味着提供路径上最近可实现的最佳性能的可靠历史记录,以便流在决定探测路径的容量时,可以快速而可靠地重新探测该性能级别。

3.4.2.2. 短期模型

BBR 的第二个目标是对每个拥塞信号(包括损耗)做出反应,就好像它可能表明拥塞持续/长期增加和/或流可用带宽减少一样,因为情况可能确实如此。

3.4.2.3. 时间尺度策略

BBR 按顺序交替使用大部分时间使用短期模型保守地尊重所有拥塞信号,以防它们代表持续拥塞,但定期使用其长期模型来稳健地探测可用路径容量的限制,以防拥塞已经减弱并且有更多可用容量。

3.5. 控制参数概述

BBR 使用其模型来控制连接的发送行为。BBR 不使用单一控制参数,例如 Reno 和 CUBIC 拥塞控制算法中限制飞行中数据量的 cwnd 参数,而是使用三个不同的控制参数:

  1. 起搏率:BBR 发送数据的最大速率。
  2. 发送量程:传输发送方实现可能需要作为一个单元传输的任何聚合的最大大小,以分摊每个数据包的传输开销。
  3. cwnd:BBR 在任何时候允许在网络中飞行的最大数据量。

3.6. 环境和使用

BBR 是一种拥塞控制算法,与传输层和链路层技术无关,只需要发送端更改,不需要更改网络。BBR 的开源实现可用于 TCP [RFC793] 和 QUIC [RFC9000] 传输协议,这些实现已在生产环境中用于大量 Internet 流量。BBR 的开源实现也可用于 DCCP [RFC4340] [draft-romo-iccrg-ccid5]。

4. 详细算法

4.1. 状态机

BBR 实现了一个状态机,该状态机使用网络路径模型来指导其决策,并使用控制参数来执行其决策。

4.1.1. 状态转换图

以下状态转换图总结了控制流以及不同状态之间的关系:

             |
             V
    +---> Startup  ------------+
    |        |                 |
    |        V                 |
    |     Drain  --------------+
    |        |                 |
    |        V                 |
    +---> ProbeBW_DOWN  -------+
    | ^      |                 |
    | |      V                 |
    | |   ProbeBW_CRUISE ------+
    | |      |                 |
    | |      V                 |
    | |   ProbeBW_REFILL  -----+
    | |      |                 |
    | |      V                 |
    | |   ProbeBW_UP  ---------+
    | |      |                 |
    | +------+                 |
    |                          |
    +---- ProbeRTT <-----------+
4.1.2. 状态机操作概述

启动时,BBR 探测以尝试快速构建网络路径的模型;为了适应路径或其流量的后续更改,BBR 必须继续探测以更新其模型。如果可用的瓶颈带宽增加,BBR 必须更快地发送才能发现这一点。同样,如果往返传播延迟发生变化,则 BDP 会发生变化,因此 BBR 必须发送较慢才能在低于新 BDP 的飞行中测量新BBR.min_rtt。因此,BBR 的状态机会定期运行顺序实验,发送速度更快以检查 BBR.bw 增加,或者发送速度较慢以产生带宽、耗尽队列并检查BBR.min_rtt减少。这些实验的频率、幅度、持续时间和结构根据已知的内容(启动或稳态)和应用程序发送行为(间歇性或连续性)而有所不同。

此状态机有几个目标:

  • 通过有效利用可用带宽实现高吞吐量。
  • 通过保持队列有限且较小,实现低延迟和数据包丢失率。
  • 以大致公平的方式与其他流共享带宽。
  • 将样本提供给模型估计器以刷新和更新模型。
4.1.3. 状态机策略

在 BBR 框架中,在任何给定时间,发送者都可以选择以下策略之一:

  • 加速:发送速度快于网络传输数据的速度:探测流可用的最大带宽
  • 巡航:以与网络传输数据相同的速率发送:尝试将发送速率与流的当前可用带宽相匹配,以尝试在不增加队列压力的情况下实现可用带宽的高利用率
  • 减速:发送速度慢于网络传输数据的速度:减少飞行中的数据量,具有许多重叠的动机:

    • 减少排队延迟:减少排队延迟,减少请求/响应交叉流量(例如 RPC、Web 流量)的延迟。
    • 减少数据包丢失:减少数据包丢失,减少请求/响应交叉流量(例如 RPC、Web 流量)的尾部延迟,并改善与 Reno/CUBIC 的共存。
    • 探测BBR.min_rtt:探测路径的BBR.min_rtt
    • 带宽收敛:通过在瓶颈链路或瓶颈缓冲区中保留未使用的容量,帮助带宽公平收敛,允许发送速率可能较低的其他流发现并利用未使用的容量
    • 突发容忍度:允许交叉流量的突发到达(例如短 Web 或 RPC 请求)能够共享瓶颈链路,而不会导致过多的排队延迟或数据包丢失

在 BBR 流的整个生命周期中,它会按顺序循环遍历所有三种策略,以测量网络路径并尝试优化其操作点。

BBR 的状态机使用两种控制机制。它主要使用 pacing_gain(请参阅“步调速率”部分),该控制数据包相对于 BBR.bw 发送的速度。pacing_gain > 1 会减少数据包间时间并增加飞行时间。pacing_gain < 1 具有相反的效果,增加了数据包之间的时间,同时旨在减少飞行中的时间。其次,如果状态机需要快速将飞行中减少到特定的绝对值,它使用 cwnd。

4.2. 算法组织

BBR 算法是一种事件驱动的算法,它对以下事件执行步骤:连接初始化、每个 ACK 、每个量子的传输以及丢失检测事件。下面将介绍下面引用的所有子步骤。

4.2.1. 初始化

在传输连接初始化时,BBR 将执行其初始化步骤:

  BBROnInit():
    init_windowed_max_filter(filter=BBR.MaxBwFilter, value=0, time=0)
    BBR.min_rtt = SRTT ? SRTT : Inf
    BBR.min_rtt_stamp = Now()
    BBR.probe_rtt_done_stamp = 0
    BBR.probe_rtt_round_done = false
    BBR.prior_cwnd = 0
    BBR.idle_restart = false
    BBR.extra_acked_interval_start = Now()
    BBR.extra_acked_delivered = 0
    BBRResetCongestionSignals()
    BBRResetLowerBounds()
    BBRInitRoundCounting()
    BBRInitFullPipe()
    BBRInitPacingRate()
    BBREnterStartup()
4.2.2. 每次传输步骤

传输时,BBR 只需要检查流从空闲状态重新启动的情况:

  BBROnTransmit():
    BBRHandleRestartFromIdle()
4.2.3. 每 ACK 步骤

在每个 ACK 上,BBR 算法执行以下 BBRUpdateOnACK() 步骤,以更新其网络路径模型、更新其状态机并调整其控制参数以适应更新的模型:

  BBRUpdateOnACK():
    BBRUpdateModelAndState()
    BBRUpdateControlParameters()

  BBRUpdateModelAndState():
    BBRUpdateLatestDeliverySignals()
    BBRUpdateCongestionSignals()
    BBRUpdateACKAggregation()
    BBRCheckStartupDone()
    BBRCheckDrain()
    BBRUpdateProbeBWCyclePhase()
    BBRUpdateMinRTT()
    BBRCheckProbeRTT()
    BBRAdvanceLatestDeliverySignals()
    BBRBoundBWForModel()

  BBRUpdateControlParameters():
    BBRSetPacingRate()
    BBRSetSendQuantum()
    BBRSetCwnd()
4.2.4. 每次损失步骤

在每个数据包丢失事件中,如果某个序列范围“数据包”被标记为丢失,BBR 算法将执行以下 BBRUpdateOnLoss() 步骤以更新其网络路径模型

  BBRUpdateOnLoss(packet):
    BBRHandleLostPacket(packet)

4.3. 状态机操作

4.3.1. 启动
4.3.1.1. 启动动态

当 BBR 流启动时,它会在 Startup 和 Drain 状态下执行第一个(也是最快速)顺序探测/排水过程。目前,网络链路带宽的范围至少为 11 个数量级,从几 bps 到 200 Gbps。为了快速学习 BBR.max_bw,考虑到这个巨大的探索范围,BBR 的 Startup 状态对速率空间进行指数搜索,每轮发送速率翻倍。这将在 O(log_2(BDP)) 往返中找到 BBR.max_bw。

为了以最平滑的方式实现这种快速探测,在 Startup BBR 中,使用最小增益值,该值将使发送速率每轮翻倍:在 Startup BBR 中,将 BBR.pacing_gain设置为 BBRStartupPacingGain (2.77) [BBRStartupPacingGain],BBR.cwnd_gain设置为 BBRStartupCwndGain (2)。

在初始化连接时,或在以后进入启动模式时,BBR 将执行以下 BBREnterStartup() 步骤:

  BBREnterStartup():
    BBR.state = Startup
    BBR.pacing_gain = BBRStartupPacingGain
    BBR.cwnd_gain = BBRStartupCwndGain

随着BBR发送率的快速增长,它获得了更高的送达率样本,BBR.max_bw增加,起搏率和cwnd都通过比例平滑增长来适应。管道满后,通常会形成一个队列,但cwnd_gain将任何队列限制为 (cwnd_gain - 1) * estimated_BDP,即大约 (2.77 - 1) * estimated_BDP = 1.77 * estimated_BDP。紧随其后的 Drain 状态旨在快速耗尽该队列。

在启动期间,BBR 使用两个估计器估计管道是否已满。第一个在 BBR.max_bw 估计值中寻找平台。第二个查找数据包丢失。以下各小节将讨论这些估计量。

  BBRCheckStartupDone():
    BBRCheckStartupFullBandwidth()
    BBRCheckStartupHighLoss()
    if (BBR.state == Startup and BBR.filled_pipe)
      BBREnterDrain()
4.3.1.2. 基于带宽平台退出启动

在启动期间,BBR 通过在 BBR.max_bw 估计值中寻找一个平台来估计管道是否已满。此“全管道”估计器的输出以 BBR.filled_pipe 进行跟踪,这是一个布尔值,用于记录 BBR 是否估计它曾经充分利用了其可用带宽(“填充管道”)。如果 BBR 注意到有几 (三) 轮尝试将交付率加倍实际上几乎没有增加(小于 25%),那么它估计它已达到 BBR.max_bw,将 BBR.filled_pipe设置为 true,退出启动并进入 Drain。

连接初始化后,完整的管道估计器将运行:

  BBRInitFullPipe():
    BBR.filled_pipe = false
    BBR.full_bw = 0
    BBR.full_bw_count = 0

在确认新数据的 ACK 中,并且当交付率样本不受应用程序限制时(参见 [draft-cheng-iccrg-delivery-rate-estimate]),如果需要,BBR 会运行“全管道”估计器,每次往返一次:

  BBRCheckStartupFullBandwidth():
    if BBR.filled_pipe or
       !BBR.round_start or rs.is_app_limited
      return  /* no need to check for a full pipe now */
    if (BBR.max_bw >= BBR.full_bw * 1.25)  /* still growing? */
      BBR.full_bw = BBR.max_bw    /* record new baseline level */
      BBR.full_bw_count = 0
      return
    BBR.full_bw_count++ /* another round w/o much growth */
    if (BBR.full_bw_count >= 3)
      BBR.filled_pipe = true

BBR 等待了三轮,以获得确凿的证据,证明发件人没有检测到接收窗口暂时施加的送达率平台。允许三轮为接收方的接收窗口自动调谐提供了时间,以打开接收窗口,并使 BBR 发送方意识到 BBR.max_bw 应该更高:在第一轮中,接收窗口自动调谐算法会增大接收窗口;在第二轮中,发送方填充更高的接收窗口;在第三轮中,发送者获得更高的送达率样本。这个三轮阈值已经通过YouTube实验数据进行了验证。

4.3.1.3. 基于丢包退出启动

BBR 用于估计瓶颈已满的第二种方法是查看持续的数据包丢失,特别是对于所有条件都满足的情况:

  • 连接已快速恢复至少一次完整的往返行程。
  • 单次完整往返时间尺度上的损失率超过 BBRLossThresh (2%)。
  • 在该往返行程中,至少会丢失 BBRStartupFullLossCnt=3 个不连续的序列范围。

如果这些条件都满足,则 BBRCheckStartupHighLoss() 设置 BBR.filled_pipe = true 并退出 Startup 并进入 Drain。

该算法会一直等到满足所有三个条件,以过滤掉突发损失中的噪声,并尝试确保瓶颈得到持续充分利用,并且已测量了整个瓶颈带宽,然后再尝试将飞行中的数据水平耗尽到估计的 BDP。

4.3.2. 排水

退出 Startup 后,BBR 将进入其 Drain 状态。在 Drain 中,BBR 旨在通过切换到远低于 1.0 的pacing_gain来快速耗尽在 Startup 中创建的任何队列,直到任何估计的队列都被耗尽。它使用一个pacing_gain,该值与启动期间使用的值相反,选择该值来尝试在一轮中耗尽队列 [BBRDrainPacingGain]:

  BBREnterDrain():
    BBR.state = Drain
    BBR.pacing_gain = 1/BBRStartupCwndGain  /* pace slowly */
    BBR.cwnd_gain = BBRStartupCwndGain      /* maintain cwnd */

在 Drain 中,当传输中的数据量小于或等于估计的 BDP,这意味着 BBR 估计队列已完全耗尽,则 BBR 退出 Drain 并进入 ProbeBW。为了实现这一点,在每个 ACK BBR 上执行:

  BBRCheckDrain():
    if (BBR.state == Drain and packets_in_flight <= BBRInflight(1.0))
      BBREnterProbeBW()  /* BBR estimates the queue was drained */
4.3.3. ProbeBW

长寿命的 BBR 流往往将绝大多数时间花在 ProbeBW 状态中。在 ProbeBW 状态下,BBR 流依次加速、减速和巡航,以测量网络路径、提高其工作点(增加吞吐量并减少队列压力),并收敛到更公平的瓶颈带宽分配。为此,流按顺序循环遍历所有三种策略:尝试发送速度快于网络传递过程、发送速度慢于网络传递过程以及以与网络交付过程相同的速率发送。为了实现这一点,ProbeBW 模式下的 BBR 流在四种 Probe bw 状态 - DOWN、CRUISE、REFILL 和 UP 之间循环 - 如下所述。

4.3.3.1. ProbeBW_DOWN

在周期的ProbeBW_DOWN阶段,BBR 流追求减速策略,试图发送比网络传输数据的速度慢,以减少传输中的数据量,以及减速策略的所有标准动机(在上面的“状态机策略”中讨论)。它通过切换到 0.9 的BBR.pacing_gain来实现这一点,以 90% 的 BBR.bw 发送。pacing_gain值 0.9 是根据ProbeBW_UP节奏增益 1.25 得出的,该增益是允许基于带宽的收敛以近似公平性的最小pacing_gain值。

退出条件:当流量估计满足以下两个条件时,流量退出此阶段并进入 CRUISE:

  • 有空余量:如果设置了 inflight_hi,则 BBR 至少在飞行中数据量小于或等于 BBRHeadroom*BBR.inflight_hi 之前保持 DOWN 状态。此约束的目标是确保在丢失信号表明飞行中数据量的上限的情况会尝试在路径中留出一些空闲空间(例如,瓶颈缓冲区中的空闲空间或瓶颈链路中的空闲时隙),这些空闲空间可供交叉流量使用(用于基于卷的带宽份额收敛和突发容忍度)。
  • 飞行中的数据量小于或等于 BBR。BDP,即流量估计它已经排空了瓶颈处的所有队列。
4.3.3.2. ProbeBW_CRUISE

在周期的ProbeBW_CRUISE阶段,BBR 流追求“巡航”策略(在上面的“状态机策略”中讨论过),试图以网络传递数据的相同速率发送。它尝试将发送速率与流的当前可用带宽相匹配,以尝试在不增加队列压力的情况下实现可用带宽的高利用率。它通过切换到 1.0 的pacing_gain来实现这一点,以 100% 的 BBR.bw 发送。 值得注意的是,在这种状态下,它通过减少BBR.bw_lo和BBR.inflight_lo来响应具体的拥塞信号(损失),因为这些信号表明,飞行中数据的可用带宽和可交付量可能已经减少,并且流量需要改变以适应, 放慢速度以匹配最新的交付流程。

退出条件:连接自适应地保持此状态,直到它决定是时候探测带宽了,此时它会进入ProbeBW_REFILL(请参阅下面的“带宽探测的时间刻度”)。

4.3.3.3. ProbeBW_REFILL

ProbeBW_REFILL状态的目标是“重新填充管道”,试图充分利用网络瓶颈,而不会产生任何显着的队列压力。

为此,BBR 首先重置短期模型参数 bw_lo 和 inflight_lo,将两者都设置为“无穷大”。这是BBR时间尺度策略(参见上文“时间尺度策略”)的关键时刻,流量转向,抛弃其保守的短期bw_lo和inflight_lo参数,并开始稳健地探测瓶颈的长期可用带宽。在此期间,bw_hi和inflight_hi(如果已设置)将限制连接。

在ProbeBW_REFILL期间,BBR 使用 1.0 的BBR.pacing_gain,以与当前估计的可用带宽匹配的速率发送一次数据包定时往返。目标是在过渡到ProbeBW_UP之前充分利用瓶颈环节,并显著增加引起损失信号的机会。令人振奋的见解是,一旦流量开始加速,发送速度超过可用带宽,它就会开始在瓶颈处构建队列。如果缓冲区足够浅,那么在第一个加速数据包到达瓶颈后不久,流量就会导致丢失信号。如果流量在引起这种损失信号之前忽略了填充管道,那么这些非常快速的超额队列信号可能会导致流量对路径容量(即 inflight_hi)的估计值被严重低估。特别是,如果流直接从ProbeBW_CRUISE过渡到ProbeBW_UP,则飞行中数据量(在发送第一个加速数据包时)通常可能仍然非常接近 CRUISE 中维护的飞行中数据量,这可能只是 BBRHeadroom*inflight_hi。

退出条件:流在一次数据包定时往返后退出ProbeBW_REFILL,并进入 UP。这是因为在发送ProbeBW_REFILL一次完整的往返行程后,流(如果不是应用程序限制)有机会在其 BBR.bw 估计允许的情况下在传输中放置尽可能多的数据包。相应地,此时流开始看到带宽样本反映其ProbeBW_REFILL行为,这可能会使过多的数据处于动态状态。

4.3.3.4. ProbeBW_UP

ProbeBW_REFILL重新填充管道后,ProbeBW_UP使用 1.25 的BBR.pacing_gain来探测可用带宽的可能增加,发送速度比当前估计的可用带宽快。

如果流未设置 BBR.inflight_hi 或 BBR.bw_hi,它会尝试将飞行中的数据量提高到至少 BBR.pacing_gain * BBR.bdp = 1.25 * BBR.bdp;请注意,如果BBR.min_rtt很小(例如在 LAN 上),这可能需要超过 BBR.min_rtt。

如果流量已设定 BBR.inflight_hi 或 BBR.bw_hi,则它会根据这些限制移动到工作点,然后使用以下方法逐渐增加音量上限 (BBR.inflight_hi) 和速率上限 (BBR.bw_hi):

  • bw_hi:如果最新测量的带宽样本大于 bw_hi 且样本的损失率不高于 BBRLossThresh,则流量bw_hi提高到最新测量的带宽样本。
  • inflight_hi:这种流动在ProbeBW_UP中以一种起初缓慢和谨慎的方式提高inflight_hi,但随着时间的推移越来越迅速和大胆。最初的谨慎是出于这样一个事实,即给定的 BBR 流可能与数千个其他流共享一个浅缓冲区,因此流可用的缓冲区空间可能非常狭窄 - 即使只是一个数据包。随着时间的推移,增长越来越快的原因是,在高速广域网中,可用带宽(以及估计的 BDP)的增加可能需要流量将其飞行数据量增加多达 O(1,000,000);即使是非常典型的 BDP,如 10Gbps * 100ms,也是 82,563 个数据包。BBR 采用的方法是,每次往返时,添加剂增加到 BBR.inflight_hi呈指数级翻倍;在每次连续的往返行程中,inflight_hi增长 1、2、4、8、16 等,增加的幅度均匀分布在整个往返行程中。这有助于使 BBR 能够在 O(log(BDP)) 往返行程中使用更大的 BDP,从而实现可扩展利用新可用带宽的设计目标。

退出条件:当满足以下任何条件时,BBR 流ProbeBW_UP带宽探测结束并转换为 ProbeBW_DOWN 以尝试排空瓶颈队列:

  1. 估计队列:流已处于ProbeBW_UP状态至少 1*min_rtt,并且估计队列足够高,以至于流判断它已稳健地探测了可用带宽 (packets_in_flight > 1.25 * BBR.bdp)。
  2. 亏损:当前亏损率超过BBRLossThresh(2%)。
4.3.3.5. 带宽探测的时间尺度

选择探测带宽的时间尺度与如何与传统 Reno/CUBIC 流共存的问题有关,因为探测带宽存在导致数据包丢失的重大风险,而导致数据包丢失会显着限制此类传统 Reno/CUBIC 流的吞吐量。

4.3.3.5.1. 带宽探测以及与Reno/CUBIC的共存

BBR 有一个与 Reno/CUBIC 共存的明确策略:尝试以某种方式运行,以便与 BBR 共存的 Reno/CUBIC 流可以在它们今天所处的主要上下文中继续正常工作:

  • 数据中心内/LAN 流量:我们希望 Reno/CUBIC 能够在 100M 到 40G 企业和数据中心以太网中表现良好

    • BDP = 40 Gbps * 20 微秒/(1514 字节)~= 66 个数据包
  • 公共互联网最后一英里流量:我们希望 Reno/CUBIC 能够在 30ms 的 RTT 下支持高达 25Mbps(对于 4K 视频),这是大型视频业务中常见 CDN 的典型参数:

    • BDP = 25Mbps * 30 ms /(1514 字节)~= 62 个数据包

实现这些目标的挑战在于,Reno/CUBIC需要长时间无损失才能利用大型BDP。好消息是,在Reno/CUBIC目前运行良好的环境中(如上所述),BDP很小,大约~100个数据包或更少。

4.3.3.5.2. 双时间尺度的共存方法

BBR策略包括几个方面:

  1. 最高优先级是估计相关 BBR 流可用的带宽。
  2. 其次,给定的 BBR 流(在一定范围内)调整其探测带宽的频率,并故意冒着数据包丢失的风险,以使 Reno/CUBIC 达到的带宽至少与给定的 BBR 流一样高。

为了适应带宽探测的频率,BBR 考虑了两个时间尺度:BBR 原生时间尺度和有界 Reno 意识时间尺度:

  • T_bbr:BBR-原生时间尺度

    • T_bbr = 在 2 到 3 秒之间均匀随机分布
  • T_reno:Reno-共存时间尺度

    • T_reno_bound = pick_randomly_either({62, 63})
    • reno_bdp = 最小值(BBR.bdp, cwnd)
    • T_reno = min(reno_bdp, T_reno_bound) 往返行程
  • T_probe:带宽探测 UP 阶段之间的时间:

    • T_probe = 最小值(T_bbr, T_reno)

这种双重时间尺度方法类似于 CUBIC 使用的方法,CUBIC 具有由立方曲线给出的 CUBIC 原生时间尺度,以及一个“Reno 仿真”模块,该模块估计 cwnd 将给流 Reno 等效吞吐量。在任何给定的时刻,CUBIC都会选择更激进的策略所暗示的cwnd。

我们对 T_bbr 和 T_reno 参数进行随机化,以实现更好的混合和公平性收敛。

4.3.3.5.3. 选择常数参数的设计注意事项

我们设计的BBR本地带宽间探针挂钟时间的最大挂钟边界,T_bbr为:

  • 高于 2 秒,以尽量避免造成足够长的损失,以允许 RTT=30ms 的 Reno 流获得 25Mbps(4K 视频)吞吐量。对于此工作负载,鉴于 Reno 锯齿将 cwnd 从大约 BDP 提高到 2*BDP,每次往返 1 MSS,带宽间探测时间必须至少为:BDP * RTT = 25Mbps * .030 秒/(1514 字节)* 0.030 秒 = 1.9 秒
  • 小于 3 秒,以确保流可以在合理的时间内开始探测,以在人类规模的交互式时间尺度上发现未使用的 bw(例如,也许来自竞争网页下载的流量现在已经完成)。

T_reno,Reno 共存时间尺度的最大往返边界选择为 62-63,并考虑以下注意事项:

  • 选择小于大约 60 的值意味着,当 BBR 流与 Reno/CUBIC 流(例如 Netflix Reno 流)在公共 Internet 宽带链路上共存时,Reno/CUBIC 流将无法获得足够的带宽来显示 4K 视频。
  • 选择大于大约 65 的值将阻止 BBR 实现其容忍每次往返 1% 损失的目标。鉴于稳态(非带宽探测)BBR 对数据包丢失率为 X% 的往返行程的响应会将发送速率降低 X%(请参阅“在数据包丢失时更新模型”部分),这意味着以 loss_rate 的速率进行 N 轮数据包丢失后的 BBR 发送速率为 (1 - loss_rate)^N。这意味着,对于在 65 次往返 ProbeBW_CRUISE 中遇到 1% 损失,然后在 ProbeBW_REFILL 和 ProbeBW_UP 中将其 cwnd(回到 BBR.inflight_hi)翻倍的流,它将能够恢复并重新探测其原始发送速率,因为:BBW.max_bw * (1 - loss_rate)^N * 2 = BBR.max_bw * (1 - .01)^65 ~= 1.04 * BBR.max_bw。也就是说,该流将能够完全响应ProbeBW_CRUISE丢包信号,同时还可以完全重新测量其ProbeBW_UP可实现的最大吞吐量。

由此产生的行为是,对于具有较小 BDP 的 BBR 流,带宽探测的时间尺度将与 Reno/CUBIC 大致相同;具有大 BDP 的流将有意比 Reno/CUBIC 更快/更频繁地探测(低 RTT 流大约每 62 次往返一次,高 RTT 流大约每 2-3 秒)。

上述定时带宽探测的注意事项可以按以下方式实现:

  /* Is it time to transition from DOWN or CRUISE to REFILL? */
  BBRCheckTimeToProbeBW():
    if (BBRHasElapsedInPhase(BBR.bw_probe_wait) ||
        BBRIsRenoCoexistenceProbeTime())
      BBRStartProbeBW_REFILL()
      return true
    return false

  /* Randomized decision about how long to wait until
   * probing for bandwidth, using round count and wall clock.
   */
  BBRPickProbeWait():
    /* Decide random round-trip bound for wait: */
    BBR.rounds_since_bw_probe =
      random_int_between(0, 1); /* 0 or 1 */
    /* Decide the random wall clock bound for wait: */
    BBR.bw_probe_wait =
      2sec + random_float_between(0.0, 1.0) /* 0..1 sec */

  BBRIsRenoCoexistenceProbeTime():
    reno_rounds = BBRTargetInflight()
    rounds = min(reno_rounds, 63)
    return BBR.rounds_since_bw_probe >= rounds

  /* How much data do we want in flight?
   * Our estimated BDP, unless congestion cut cwnd. */
  BBRTargetInflight()
    return min(BBR.bdp, cwnd)
4.3.3.6. ProbeBW算法细节

BBR 的 ProbeBW 算法的操作方式如下。

进入 ProbeBW 后,BBR 执行:

  BBREnterProbeBW():
    BBRStartProbeBW_DOWN()

输入每种状态的核心逻辑:

  BBRStartProbeBW_DOWN():
    BBRResetCongestionSignals()
    BBR.probe_up_cnt = Infinity /* not growing inflight_hi */
    BBRPickProbeWait()
    BBR.cycle_stamp = Now()  /* start wall clock */
    BBR.ack_phase  = ACKS_PROBE_STOPPING
    BBRStartRound()
    BBR.state = ProbeBW_DOWN

  BBRStartProbeBW_CRUISE():
    BBR.state = ProbeBW_CRUISE

  BBRStartProbeBW_REFILL():
    BBRResetLowerBounds()
    BBR.bw_probe_up_rounds = 0
    BBR.bw_probe_up_acks = 0
    BBR.ack_phase = ACKS_REFILLING
    BBRStartRound()
    BBR.state = ProbeBW_REFILL

  BBRStartProbeBW_UP():
    BBR.ack_phase = ACKS_PROBE_STARTING
    BBRStartRound()
    BBR.cycle_stamp = Now() /* start wall clock */
    BBR.state = ProbeBW_UP
    BBRRaiseInflightHiSlope()

BBR 在每次 ACK 上执行以下 BBRUpdateProbeBWCyclePhase() 逻辑,以执行 ACK 或 SACK 新数据,以推进 ProbeBW 状态机:

  /* The core state machine logic for ProbeBW: */
  BBRUpdateProbeBWCyclePhase():
    if (!BBR.filled_pipe)
      return  /* only handling steady-state behavior here */
    BBRAdaptUpperBounds()
    if (!IsInAProbeBWState())
      return /* only handling ProbeBW states here: */

    switch (state)

    ProbeBW_DOWN:
      if (BBRCheckTimeToProbeBW())
        return /* already decided state transition */
      if (BBRCheckTimeToCruise())
        BBRStartProbeBW_CRUISE()

    ProbeBW_CRUISE:
      if (BBRCheckTimeToProbeBW())
        return /* already decided state transition */

    ProbeBW_REFILL:
      /* After one round of REFILL, start UP */
      if (BBR.round_start)
        BBR.bw_probe_samples = 1
        BBRStartProbeBW_UP()

    ProbeBW_UP:
      if (BBRHasElapsedInPhase(BBR.min_rtt) and
          inflight > BBRInflight(BBR.max_bw, 1.25))
        BBRStartProbeBW_DOWN()

实现 ProbeBW 状态机的辅助逻辑:

  IsInAProbeBWState()
    state = BBR.state
    return (state == ProbeBW_DOWN or
            state == ProbeBW_CRUISE or
            state == ProbeBW_REFILL or
            state == ProbeBW_UP)

  /* Time to transition from DOWN to CRUISE? */
  BBRCheckTimeToCruise():
    if (inflight > BBRInflightWithHeadroom())
      return false /* not enough headroom */
    if (inflight <= BBRInflight(BBR.max_bw, 1.0))
      return true  /* inflight <= estimated BDP */

  BBRHasElapsedInPhase(interval):
    return Now() > BBR.cycle_stamp + interval

  /* Return a volume of data that tries to leave free
   * headroom in the bottleneck buffer or link for
   * other flows, for fairness convergence and lower
   * RTTs and loss */
  BBRInflightWithHeadroom():
    if (BBR.inflight_hi == Infinity)
      return Infinity
    headroom = max(1, BBRHeadroom * BBR.inflight_hi)
      return max(BBR.inflight_hi - headroom,
                 BBRMinPipeCwnd)

  /* Raise inflight_hi slope if appropriate. */
  BBRRaiseInflightHiSlope():
    growth_this_round = 1MSS << BBR.bw_probe_up_rounds
    BBR.bw_probe_up_rounds = min(BBR.bw_probe_up_rounds + 1, 30)
    BBR.probe_up_cnt = max(cwnd / growth_this_round, 1)

  /* Increase inflight_hi if appropriate. */
  BBRProbeInflightHiUpward():
    if (!is_cwnd_limited or cwnd < BBR.inflight_hi)
      return  /* not fully using inflight_hi, so don't grow it */
   BBR.bw_probe_up_acks += rs.newly_acked
   if (BBR.bw_probe_up_acks >= BBR.probe_up_cnt)
     delta = BBR.bw_probe_up_acks / BBR.probe_up_cnt
     BBR.bw_probe_up_acks -= delta * BBR.bw_probe_up_cnt
     BBR.inflight_hi += delta
   if (BBR.round_start)
     BBRRaiseInflightHiSlope()

  /* Track ACK state and update BBR.max_bw window and
   * BBR.inflight_hi and BBR.bw_hi. */
  BBRAdaptUpperBounds():
    if (BBR.ack_phase == ACKS_PROBE_STARTING and BBR.round_start)
      /* starting to get bw probing samples */
      BBR.ack_phase = ACKS_PROBE_FEEDBACK
    if (BBR.ack_phase == ACKS_PROBE_STOPPING and BBR.round_start)
      /* end of samples from bw probing phase */
      if (IsInAProbeBWState() and !rs.is_app_limited)
        BBRAdvanceMaxBwFilter()

    if (!CheckInflightTooHigh())
      /* Loss rate is safe. Adjust upper bounds upward. */
      if (BBR.inflight_hi == Infinity or BBR.bw_hi == Infinity)
        return /* no upper bounds to raise */
      if (rs.tx_in_flight > BBR.inflight_hi)
        BBR.inflight_hi = rs.tx_in_flight
      if (rs.delivery_rate > BBR.bw_hi)
        BBR.bw_hi = rs.bw
      if (BBR.state == ProbeBW_UP)
        BBRProbeInflightHiUpward()
4.3.4. ProbeRTT
4.3.4.1. ProbeRTT概述

为了帮助探测BBR.min_rtt,BBR 流根据需要进入 ProbeRTT 状态,以尝试合作以定期排空瓶颈队列 - 从而提高他们对卸载双向传播延迟的BBR.min_rtt估计。

一个关键点是,在 BBR 提高其BBR.min_rtt估计值(这反过来又会提高其最大允许 cwnd)之前,它首先进入 ProbeRTT,以尝试做出协同一致的努力,以排空瓶颈队列并进行稳健的BBR.min_rtt测量。这使得 BBR 流集合的BBR.min_rtt估计能够收敛,从而避免不断增加的队列和 RTT 样本的反馈循环。

ProbeRTT 状态与BBR.min_rtt估计协同工作。每 ProbeRTTInterval = 5 秒,流最多一次进入 ProbeRTT,通过将其cwnd_gain设置为 BBRProbeRTTCwndGain = 0.5 来减速,以将其飞行数据量减少到其估计 BDP 的一半,以尝试允许流测量卸载的双向传播延迟。

使 MinRTTFilterLen 大约是 ProbeRTTInterval 的两倍,有两个主要动机。首先,这确保了在 ProbeRTT 事件期间,流量将“记住”它在上一次 ProbeRTT 事件期间测量的BBR.min_rtt值,从而为 cwnd = 0.5*bdp 计算提供稳健的 bdp 估计,从而增加了完全耗尽瓶颈队列的可能性。其次,这使得流程的BBR.min_rtt过滤器窗口通常包含来自两个 ProbeTT 集的 RTT 样本,从而提供更可靠的估计。

ProbeRTT 的算法如下:

入场条件:在 ProbeRTT 本身以外的任何状态下,如果 BBR.probe_rtt_min_delay 估计值未更新(即,通过获得较低的 RTT 测量值)超过 ProbeRTTInterval = 5 秒,则 BBR 进入 ProbeRTT 并将BBR.cwnd_gain降低到 BBRProbeRTTCwndGain = 0.5。

退出条件:在 BBRProbeRTTCwndGain*BBR.bdp 上保持飞行中数据量至少 ProbeRTTDuration (200 ms) 和至少一次往返行程后,BBR 离开 ProbeRTT 并转换到 ProbeBW(如果它估计管道已填充),否则将启动。

4.3.4.2. ProbeRTT设计原理

BBR 旨在让 ProbeRTT 牺牲不超过流可用带宽的大约 2%。它还被设计为将绝大多数时间(至少大约 96%)花费在 ProbeBW 中,其余时间花费在 ProbeRTT 中,这是基于一组权衡。ProbeRTT 持续时间足够长(至少 ProbeRTTDuration = 200 毫秒),以允许具有不同 RTT 的流具有重叠的 ProbeRTT 状态,同时仍然足够短,可以将 ProbeRTT 的 cwnd 上限的吞吐量损失限制在大约 2%,平均吞吐量目标为:

  throughput = (200ms*0.5*BBR.bw + (5s - 200ms)*BBR.bw) / 5s
             = (.1s + 4.8s)/5s * BBR.bw = 0.98 * BBR.bw

如上所述,BBR 的BBR.min_rtt滤镜窗口 MinRTTFilterLen 和 ProbeRTT 状态之间的时间间隔 ProbeRTTInterval 协同工作。BBR 使用等于或长于 ProbeRTTInterval 的 MinRTTFilterLen 来允许筛选器窗口包含至少一个 ProbeRTT。

为了允许与其他 BBR 流进行协调,每个流都必须使用 5 秒的标准 ProbeRTTInterval。

5 秒的 ProbeRTTInterval 足够短,以便在流量级别或路由发生变化时允许快速收敛,但足够长,以便交互式应用程序(例如,Web、远程过程调用、视频块)通常在窗口内具有自然静默或低速率期,其中流量的速率足够低,足够长的时间以耗尽其在瓶颈中的队列。然后,BBR.probe_rtt_min_delay滤波器会适时地获取这些测量值,并且无需 ProbeRTT 即可刷新BBR.probe_rtt_min_delay估计值。这样,如果有多个批量流在整个 ProbeRTTInterval 窗口上忙于发送,则流通常只需要支付 2% 的吞吐量损失。

作为一种优化,当从空闲重新启动并发现BBR.probe_rtt_min_delay已过期时,BBR 不会进入 ProbeRTT;空闲被视为足以协调以排空队列的尝试。

4.3.4.3. 计算rs.rtt RTT样本

在传输每个数据包时,BBR(或相关的传输协议)在每个数据包数据中存储数据包的挂钟计划传输时间(以packet.departure_time为单位)(有关如何计算此时间,请参阅“起搏率:BBR.pacing_rate”部分)。

对于每个新确认某些数据的 ACK(无论是累积数据还是选择性数据),发送方的 BBR 实现(或关联的传输协议实现)都会尝试计算 RTT 样本。发送方必须考虑某些传输协议中可能出现的任何潜在重新传输歧义。如果某些已确认的数据未被重新传输,或者某些数据被重新传输,但发送方仍然可以明确地确定数据的 RTT(例如,如果传输支持 [RFC7323] TCP 时间戳或等效机制),则发送方将计算 RTT 样本 rs.rtt,如下所示:

  rs.rtt = Now() - packet.departure_time
4.3.4.4. ProbeRTT逻辑

在每个 ACK 上,BBR 执行 BBRUpdateMinRTT() 以更新其 ProbeRTT 调度状态(BBR.probe_rtt_min_delay 和 BBR.probe_rtt_min_stamp)及其BBR.min_rtt估计值:

  BBRUpdateMinRTT()
    BBR.probe_rtt_expired =
      Now() > BBR.probe_rtt_min_stamp + ProbeRTTInterval
    if (rs.rtt >= 0 and
        (rs.rtt < BBR.probe_rtt_min_delay or
         BBR.probe_rtt_expired))
       BBR.probe_rtt_min_delay = rs.rtt
       BBR.probe_rtt_min_stamp = Now()

    min_rtt_expired =
      Now() > BBR.min_rtt_stamp + MinRTTFilterLen
    if (BBR.probe_rtt_min_delay < BBR.min_rtt or
        min_rtt_expired)
      BBR.min_rtt       = BBR.probe_rtt_min_delay
      BBR.min_rtt_stamp = BBR.probe_rtt_min_stamp

这里BBR.probe_rtt_expired是一个布尔值,记录BBR.probe_rtt_min_delay是否已过期以及是否应通过应用程序空闲期或过渡到 ProbeRTT 状态进行刷新。

在每个 ACK 上,BBR 执行 BBRCheckProbeRTT() 以处理与 ProbeRTT 状态相关的步骤,如下所示:

  BBRCheckProbeRTT():
    if (BBR.state != ProbeRTT and
        BBR.probe_rtt_expired and
        not BBR.idle_restart)
      BBREnterProbeRTT()
      BBRSaveCwnd()
      BBR.probe_rtt_done_stamp = 0
      BBR.ack_phase = ACKS_PROBE_STOPPING
      BBRStartRound()
    if (BBR.state == ProbeRTT)
      BBRHandleProbeRTT()
    if (rs.delivered > 0)
      BBR.idle_restart = false

  BBREnterProbeRTT():
    BBR.state = ProbeRTT
    BBR.pacing_gain = 1
    BBR.cwnd_gain = BBRProbeRTTCwndGain  /* 0.5 */

  BBRHandleProbeRTT():
    /* Ignore low rate samples during ProbeRTT: */
    MarkConnectionAppLimited()
    if (BBR.probe_rtt_done_stamp == 0 and
        packets_in_flight <= BBRProbeRTTCwnd())
      /* Wait for at least ProbeRTTDuration to elapse: */
      BBR.probe_rtt_done_stamp =
        Now() + ProbeRTTDuration
      /* Wait for at least one round to elapse: */
      BBR.probe_rtt_round_done = false
      BBRStartRound()
    else if (BBR.probe_rtt_done_stamp != 0)
      if (BBR.round_start)
        BBR.probe_rtt_round_done = true
      if (BBR.probe_rtt_round_done)
        BBRCheckProbeRTTDone()

  BBRCheckProbeRTTDone():
    if (BBR.probe_rtt_done_stamp != 0 and
        Now() > BBR.probe_rtt_done_stamp)
      /* schedule next ProbeRTT: */
      BBR.probe_rtt_min_stamp = Now()
      BBRRestoreCwnd()
      BBRExitProbeRTT()

  MarkConnectionAppLimited():
    C.app_limited =
      (C.delivered + packets_in_flight) ? : 1
4.3.4.5. 退出ProbeRTT

退出 ProbeRTT 时,如果 BBR 估计管道已填充,则 BBR 将转换为 ProbeBW,否则将启动。

当从 ProbeRTT 过渡出来时,BBR 会调用 BBRResetLowerBounds() 来重置下限,因为在 ProbeRTT 中遇到的任何拥塞都可能使短期模型远低于路径的容量。

但该算法在为下一个带宽探测计时时非常谨慎:在 ProbeRTT 之后提高飞行可能会导致损失,因此该算法通过在 ProbeBW_DOWN() 开始周期来重置带宽探测时钟。但是,作为优化,由于连接正在退出 ProbeRTT,我们知道 infligh 已经低于估计的 BDP,因此连接可以立即继续进行 ProbeBW_CRUISE。

总而言之,退出 ProbeRTT 的逻辑如下:

  BBRExitProbeRTT():
    BBRResetLowerBounds()
    if (BBR.filled_pipe)
      BBRStartProbeBW_DOWN()
      BBRStartProbeBW_CRUISE()
    else
      BBREnterStartup()

4.4. 从空闲重新启动

4.4.1. 在ProbeBW中设置起搏率

当从 ProbeBW 状态的空闲重新启动时,BBR 保持其 cwnd 原样,并以正好 BBR.bw 的速度调整数据包,旨在尽快返回到其速率平衡和完整管道的目标操作点。具体来说,如果流的 BBR.state 是 ProbeBW,并且流是应用程序限制的,并且当前没有正在传输的数据包,则此时流发送一个或多个 BBR 设置为BBR.pacing_rate的数据包正好 BBR.bw。更准确地说,BBR 算法在发送流数据包之前,会在 BBRHandleRestartFromIdle() 中执行以下步骤。

[RFC5681] 的“重新启动空闲连接”部分建议从初始窗口缓慢启动,从而从空闲状态重新启动。然而,这种方法假设了一种拥塞控制算法,该算法没有估计瓶颈带宽,也没有节奏,因此依赖于由 ACK 时钟驱动的慢启动。使用“空闲后慢启动”方法实现充分利用所需的长时间 (log_2(BDP)*RTT) 延迟导致许多大型部署禁用此机制,从而导致采用“BDP 规模线速突发”方法。BBR 不是这两种方法,而是通过在 BBR.bw 时起搏来重新启动,通常在仅经过一BBR.min_rtt后即可实现近似的速率平衡和完整的管道。

4.4.2. 检查 ProberRTT 完成

作为一项优化,当从空闲重新启动时,BBR 会检查连接是否在 ProbeRTT 中,并且是否满足 ProbeRTT 的退出条件。如果连接在 ProbeRTT 期间空闲,那么它通常在重新启动时已满足这些退出条件,以便连接可以在开始传输新的数据飞行之前将 cwnd 恢复到其全部值。

4.4.3. 逻辑

BBR 算法在发送流数据包之前,在 BBRHandleRestartFromIdle() 中执行以下步骤:

  BBRHandleRestartFromIdle():
    if (packets_in_flight == 0 and C.app_limited)
      BBR.idle_restart = true
         BBR.extra_acked_interval_start = Now()
      if (IsInAProbeBWState())
        BBRSetPacingRateWithGain(1)
      else if (BBR.state == ProbeRTT)
        BBRCheckProbeRTTDone()

4.5. 更新网络路径模型参数

BBR 是一种基于模型的拥塞控制算法:它基于传输流经过的网络路径的显式模型。以下是每个参数的摘要,包括其含义以及算法如何计算和使用其值。我们可以将参数分为三组:

  • 核心状态机参数
  • 用于对数据速率进行建模的参数
  • 用于对飞行中数据量进行建模的参数
4.5.1. BBR.round_count:跟踪分组定时往返

BBR 算法的几个方面依赖于对“数据包定时”往返的进度进行计数,这些往返从某个段的传输开始,然后在确认该段时结束。BBR.round_count是到目前为止经过的这些“数据包定时”往返次数的计数。BBR 使用此虚拟BBR.round_count,因为它比使用挂钟时间更强大。具体而言,由于应用程序空闲、RTT 的变化或重传超时的计时器延迟,可能会经过任意的挂钟时间间隔,从而导致挂钟定时模型参数估计“超时”或过快“忘记”,无法提供稳健性。

BBR 通过使用以下伪代码记录有关哨兵数据包的状态,并等待在该哨兵数据包之后发送的任何数据包的 ACK,对数据包定时往返行程进行计数:

连接初始化时:

  BBRInitRoundCounting():
    BBR.next_round_delivered = 0
    BBR.round_start = false
    BBR.round_count = 0

在发送每个数据包时,速率估计算法 [draft-cheng-iccrg-delivery-rate-estimation] 会记录迄今为止确认为已传递的数据量:

  packet.delivered = C.delivered

在收到给定数据包的 ACK 后,速率估计算法 [draft-cheng-iccrg-delivery-rate-estimation] 会更新迄今为止确认为已传递的数据量:

    C.delivered += packet.size

在收到给定数据包的 ACK 后,BBR 算法首先执行以下逻辑,以查看是否已经过往返,如果已过去,则增加经过的此类往返次数:

  BBRUpdateRound():
    if (packet.delivered >= BBR.next_round_delivered)
      BBRStartRound()
      BBR.round_count++
      BBR.rounds_since_probe++
      BBR.round_start = true
    else
      BBR.round_start = false

  BBRStartRound():
    BBR.next_round_delivered = C.delivered
4.5.2. BBR.max_bw:估计的最大带宽

BBR.max_bw 是 BBR 对传输流的数据传输可用的最大瓶颈带宽的估计值。在任何时候,传输连接的数据传输都会遇到一些最慢的链路或瓶颈。瓶颈的传输速率决定了连接的最大数据传输速率。BBR 试图将其发送速率与此瓶颈传递速率紧密匹配,以帮助寻求“速率平衡”,即流在瓶颈处的数据包到达率等于离开率。瓶颈率在连接的生命周期内会发生变化,因此 BBR 会不断使用最近的信号估计 BBR.max_bw。

4.5.2.1. 用于估计 BBR.max_bw 的交付率样本

由于计算分娩率样本是微妙的,并且样本与拥塞控制无关是有用的,因此 BBR 用于测量每个单一分娩率样本的方法在单独的 Internet 草案 [draft-cheng-iccrg-delivery-rate-estimation] 中进行了指定。

4.5.2.2. BBR.max_bw Max滤波器

由于物理传输过程中的随机变化(例如无线电链路层噪声)或队列或沿网络路径的随机变化引入“噪声”,传输速率样本通常低于可用于流量的典型瓶颈带宽。为了过滤这些影响,BBR 使用最大过滤器:BBR 使用连接在最近历史记录中看到的窗口化最大近期传递率样本来估计 BBR.max_bw。

BBR.max_bw max 滤波器窗口涵盖过去两个 ProbeBW 周期的时间段。BBR.max_bw 最大滤波器窗口长度是由几个考虑因素之间的权衡驱动的:

  • 它足够长,至少可以覆盖一个完整的 ProbeBW 周期(请参阅“ProbeBW”部分)。这可确保窗口至少包含一些传递率样本,这些样本是通过超统一pacing_gain(大于 1.0 的pacing_gain)传输的数据的结果。这种超统一传输速率样本有助于揭示路径的底层可用带宽,即使由于聚合延迟、可变交叉流量的排队延迟、未校正损失的有损链路层或短期缓冲区耗尽(例如,浅层缓冲区中的短暂重合突发)导致的传输速率不足而产生噪声。
  • 它的目标是足够长,以涵盖由于上述噪声源导致的网络传输率的短期波动。特别是,无线电链路层(例如,wifi和蜂窝技术)的传输速率可能变化很大,并且滤波器窗口需要足够长的时间以记住“良好”的传输速率样本,以便对这种变化具有鲁棒性。
  • 它的目标是足够短,以便及时响应流可用带宽的持续减少,无论是因为其他流正在使用更大份额的瓶颈,还是由于第 1 层或第 2 层更改、策略更改或路由更改导致瓶颈链路服务速率降低。在上述任何一种情况下,穿越瓶颈的现有 BBR 流都应及时减少其 BBR.max_bw 估计值,从而降低速率和飞行数据的节奏,以便将发送行为与新的可用带宽相匹配。
4.5.2.3. BBR.max_bw 和应用限制的交付率样本

传输可以受到应用程序的限制,这意味着传输速率受应用程序的限制,而不是拥塞控制算法的限制。由于请求/响应流量,这种情况很常见。当有传输机会但没有数据可发送时,传送速率采样器将相应的带宽样本标记为应用受限 [draft-cheng-iccrg-delivery-rate-estimation]。BBR.max_bw 估计器会仔细决定要在带宽模型中包含哪些样本,以确保 BBR.max_bw 反映的是网络限制,而不是应用限制。默认情况下,估计器会丢弃受应用程序限制的样本,因为根据定义,它们反映了应用程序限制。但是,如果测得的传递速率恰好大于当前的 BBR.max_bw 估计值,则估计器会使用应用受限的样本,因为这表示当前的 BBR。Max_bw估计太低了。

4.5.2.4. 更新 BBR.max_bw Max 过滤器

对于每个确认某些数据包已传递的 ACK,BBR 调用 BBRUpdateMaxBw() 以更新 BBR.max_bw 估计器,如下所示(此处rs.delivery_rate是从正在处理的 ACK 获取的传递率样本,如 [draft-cheng-iccrg-delivery-rate-estimateimation] 中指定):

  BBRUpdateMaxBw()
    BBRUpdateRound()
    if (rs.delivery_rate >= BBR.max_bw || !rs.is_app_limited)
        BBR.max_bw = update_windowed_max_filter(
                      filter=BBR.MaxBwFilter,
                      value=rs.delivery_rate,
                      time=BBR.cycle_count,
                      window_length=MaxBwFilterLen)
4.5.2.5. BBR.max_bw Max滤波器的跟踪时间

BBR 使用虚拟(非挂钟)时间跟踪 BBR.max_bw 滤波器窗口的时间,该时间是通过计算 ProbeBW 周期的周期性进展来跟踪的。每次通过 Probe bw 循环时,在退出 ProbeBW_UP(流量有最佳机会测量周期最高吞吐量的点)后一次往返,BBR 会增加 BBR.cycle_count,即 BBR.max_bw 过滤器窗口使用的虚拟时间。请注意,BBR.cycle_count只需要使用单个比特进行跟踪,因为 BBR.max_bw 滤波器只需要跟踪来自两个时隙的样本:上一个 ProbeBW 周期和当前 ProbeBW 周期:

  BBRAdvanceMaxBwFilter():
    BBR.cycle_count++
4.5.3. BBR.min_rtt:预计最短往返时间

BBR.min_rtt 是 BBR 对传输连接发送路径的往返传播延迟的估计值。路径的往返传播延迟决定了连接必须愿意以 BBR.bw 速率维持传输的最短时间,从而决定了连接达到充分利用(“全管道”)所需的飞行中所需的最小数据量。在连接的生命周期内,往返传播延迟可能会发生变化,因此 BBR 会使用最近的往返延迟样本不断估计BBR.min_rtt。

4.5.3.1. 用于估计BBR.min_rtt的往返时间样本

对于连接发送的每个数据包,BBR 都会计算一个 RTT 样本,该样本测量从发送数据包到确认该数据包的时间间隔。

在大多数情况下,适用于重传超时计算的 RTT 估计的相同注意事项和机制 [RFC6298] 适用于 BBR RTT 样本。也就是说,BBR 不使用基于重传数据包传输时间的 RTT 样本,因为这些数据包不明确,因此不可靠。此外,BBR 使用累积确认和选择性确认(如果传输支持 [RFC2018] SACK 选项或等效机制)或传输层时间戳(如果传输支持 [RFC7323] TCP 时间戳或等效机制)来计算 RTT 样本。

与重传超时的 RTT 估计的唯一不同之处在于,给定的确认会确认多个数据包。为了保守起见并安排较长的超时以避免虚假的重传,通常使用此类潜在 RTT 样本中的最大值来计算重传超时;也就是说,SRTT通常是使用传输时间最早的数据包来计算的。相比之下,为了让 BBR 尝试达到飞行中填充管道的最小数据量,BBR 在这种潜在的 RTT 样本中使用最小值;即,BBR 使用具有最新传输时间的数据包计算 RTT。

4.5.3.2. BBR.min_rtt最小滤波器

由于物理传输过程中的随机变化(如无线电链路层噪声)、网络路径上的队列、接收机的延迟确认策略、确认聚集等引入的“噪声”,RTT样本往往高于路径的往返传播延迟。因此,为了过滤掉这些影响,BBR 使用最小滤波器:BBR 使用连接在过去 MinRTTFilterLen 秒内看到的最小近期 RTT 样本来估计BBR.min_rtt。(许多相同的网络效应会降低传递率测量值,从而增加 RTT 样本,这就是为什么 BBR 的 RTT 最小滤波方法是其传递率最大滤波方法的补充。

BBR.min_rtt 分钟滤波器窗口的长度为 MinRTTFilterLen = 10 秒。这是由几个考虑因素之间的权衡驱动的:

  • MinRTTFilterLen 比 ProbeRTTInterval 长,因此它涵盖了整个 ProbeRTT 周期(请参阅下面的“ProbeRTT”部分)。这有助于确保窗口可以包含 RTT 样本,这些样本是飞行中传输的数据低于流的估计 BDP 的结果。这种RTT样本对于帮助揭示路径的潜在双向传播延迟非常重要,即使上述“噪声”效应通常会使其模糊不清。
  • MinRTTFilterLen 的目标是足够长,以避免需要经常削减飞行中和吞吐量。测量双向传播延迟要求飞行中等于或低于 BDP,这存在一定程度的利用率不足的风险,因此 BBR 使用滤波器窗口的时间足够长,以至于这种利用率不足的事件可能很少见。
  • MinRTTFilterLen 的目标是足够长,以至于许多应用程序具有“自然”的静默时刻或低利用率,可以在飞行中降低 BDP 并自然地刷新BBR.min_rtt,而无需 BBR 在飞行中强制人为切割。这适用于许多流行的应用程序,包括 Web、RPC、分块音频或视频流量。
  • MinRTTFilterLen 的目标是足够短,以便及时响应路径双向传播延迟的实际增加,例如由于路由更改,预计这些更改通常会在更长的时间尺度上发生。

BBR 实现可以使用通用的窗口最小滤波器来跟踪BBR.min_rtt。但是,通过将BBR.min_rtt估计集成到 ProbeRTT 状态机中,可以显著节省空间并提高新鲜度,因此本文档在 ProbeRTT 部分中讨论了该方法。

4.5.4. BBR.offload_budget

BBR.offload_budget是使用发送方 (TSO/GSO) 和接收方 (LRO、GRO) 主机卸载机制实现最大吞吐量所需的最小数据量的估计值,计算方式如下:

    BBRUpdateOffloadBudget():
      BBR.offload_budget = 3 * BBR.send_quantum

选择因子 3 以允许至少维持:

  • 发送主机的队列规程层中有 1 个量子
  • 1 个量子在发送主机 TSO/GSO 引擎中分段
  • 1 个量子由于接收器主机的 LRO/GRO/delayed-ACK 引擎而被重新组装或以其他方式保持未确认状态
4.5.5. BBR.extra_acked

BBR.extra_acked 是一个数据量,是对网络路径中最近聚集程度的估计。对于每个 ACK,该算法计算的估计额外 ACKed 数据样本超出了发送方在给定 BBR.bw 的时间范围内预期的往返时间范围内 ACK 的数据量。然后,它将 BBR.extra_acked计算为最后一个 BBRExtraAckedFilterLen=10 个数据包定时往返的窗口化最大样本。如果 ACK 速率低于预期带宽,则算法会估计聚合事件已终止,并将采样间隔重置为从当前时间开始。

因此,BBR.extra_acked反映了最近测量的数据量级和 ACK 聚合效应,例如共享中等 L2 跳(wifi、蜂窝、DOCSIS)的批处理和时隙,以及终端主机卸载机制(TSO、GSO、LRO、GRO)和终端主机或中间盒 ACK 抽取/稀疏。

BBR 通过 BBR.extra_acked 来增强其 cwnd,以允许连接在 ACK 间静默期间继续发送,其程度与最近测量的聚合程度相匹配。

更准确地说,这是计算的:

  BBRUpdateACKAggregation():
    /* Find excess ACKed beyond expected amount over this interval */
    interval = (Now() - BBR.extra_acked_interval_start)
    expected_delivered = BBR.bw * interval
    /* Reset interval if ACK rate is below expected rate: */
    if (BBR.extra_acked_delivered <= expected_delivered)
        BBR.extra_acked_delivered = 0
        BBR.extra_acked_interval_start = Now()
        expected_delivered = 0
    BBR.extra_acked_delivered += rs.newly_acked
    extra = BBR.extra_acked_delivered - expected_delivered
    extra = min(extra, cwnd)
    BBR.extra_acked =
      update_windowed_max_filter(
        filter=BBR.ExtraACKedFilter,
        value=extra,
        time=BBR.round_count,
        window_length=BBRExtraAckedFilterLen)
4.5.6. 丢包时更新模型

在每种状态下,BBR 都会响应(过滤的)拥塞信号,包括损失。对这些拥塞信号的响应取决于流的当前状态,因为流可以推断的信息取决于流在经历信号时流正在做什么。

4.5.6.1. 探测启动时的带宽

在 Startup 中,如果拥塞信号满足 Startup 退出条件,则流将退出 Startup 并进入 Drain。

4.5.6.2. 探测ProbeBW中的带宽

BBR 搜索可以合理地放置在网络中的最大数据量。一个关键的先决条件是,流实际上正在稳健地尝试找到那个工作点。为了实现这一点,当一个流在 ProbeBW 中,并且 ACK 覆盖了在其中一个加速阶段(REFILL 或 UP)发送的数据,并且 ACK 指示过去往返的损失率超过了队列压力目标,并且流不受应用程序限制,并且尚未响应最近 REFILL 或 UP 阶段的拥塞信号, 然后,流估计它在飞行中允许的数据量超过了与路径上的当前交付过程匹配的数据量,并减少了BBR.inflight_hi:

  /* Do loss signals suggest inflight is too high?
   * If so, react. */
  CheckInflightTooHigh():
    if (IsInflightTooHigh(rs))
      if (BBR.bw_probe_samples)
        BBRHandleInflightTooHigh()
      return true  /* inflight too high */
    else
      return false /* inflight not too high */

  IsInflightTooHigh():
    return (rs.lost > rs.tx_in_flight * BBRLossThresh)

  BBRHandleInflightTooHigh():
    BBR.bw_probe_samples = 0;  /* only react once per bw probe */
    if (!rs.is_app_limited)
      BBR.inflight_hi = max(rs.tx_in_flight,
                            BBRTargetInflight() * BBRBeta))
    If (BBR.state == ProbeBW_UP)
      BBRStartProbeBW_DOWN()

这里rs.tx_in_flight是在发送最近一次 ACK 数据包时估计正在传输的数据量。BBRBeta (0.7x) 绑定是为了试图确保 BBR 的反应不会比 CUBIC 的 0.7x 乘法减少因子更剧烈。

一些丢失检测算法,包括像 RACK [RFC8985] 这样的算法,在等待可能的重新排序解决时延迟丢失标记,可能会在丢失本身发生很久之后将数据包标记为丢失。在这种情况下,允许检测到丢失的传送序列范围的tx_in_flight可能比丢失数据包本身的tx_in_flight小得多。在这种情况下,使用前者tx_in_flight而不是后者可能会导致BBR.inflight_hi被严重低估。为了避免此类问题,BBR 会处理每个丢失检测事件,以更精确地估计丢失率通过 BBRLossThresh 的传输中数据量,并指出这可能发生在某些数据包的中途。为了估计这个值,我们可以求解以下等式中的“lost_prefix”,其中 inflight_prev 表示此数据包之前的飞行数据量,lost_prev表示在先前的飞行数据中丢失的数据:

    lost                     /  inflight                     >= BBRLossThresh
   (lost_prev + lost_prefix) / (inflight_prev + lost_prefix) >= BBRLossThresh
   /* solving for lost_prefix we arrive at: */
   lost_prefix = (BBRLossThresh * inflight_prev - lost_prev) / (1 - BBRLossThresh)

在伪代码中:

  BBRHandleLostPacket(packet):
    if (!BBR.bw_probe_samples)
      return /* not a packet sent while probing bandwidth */
    rs.tx_in_flight = packet.tx_in_flight /* inflight at transmit */
    rs.lost = C.lost - packet.lost /* data lost since transmit */
    rs.is_app_limited = packet.is_app_limited;
    if (IsInflightTooHigh(rs))
      rs.tx_in_flight = BBRInflightHiFromLostPacket(rs, packet)
      BBRHandleInflightTooHigh(rs)

  /* At what prefix of packet did losses exceed BBRLossThresh? */
  BBRInflightHiFromLostPacket(rs, packet):
    size = packet.size
    /* What was in flight before this packet? */
    inflight_prev = rs.tx_in_flight - size
    /* What was lost before this packet? */
    lost_prev = rs.lost - size
    lost_prefix = (BBRLossThresh * inflight_prev - lost_prev) /
                  (1 - BBRLossThresh)
    /* At what inflight value did losses cross BBRLossThresh? */
    inflight = inflight_prev + lost_prefix
    return inflight
4.5.6.3. 不探测带宽时

当未明确加速以探测带宽(Drain、ProbeRTT、ProbeBW_DOWN、ProbeBW_CRUISE)时,BBR 会在一定程度上减慢速度来响应损失。这是因为丢失表明,可用带宽和安全的飞行数据量最近可能已经减少,并且流量需要适应,在最新的交付过程中减慢速度。BBR 流通过减少短期模型参数 BBR.bw_lo 和 BBR.inflight_lo来实现这种响应。

当流量不探测带宽时遇到数据包丢失时,策略是逐渐适应当前测量的传输过程(在最后一次往返中通过网络路径传输的数据的速率和量)。这通常适用于:无论是在快速恢复、RTO 恢复、TLP 恢复中;无论是否受应用程序限制。

算法跟踪两个关键参数,用于衡量当前的交付过程:

BBR.bw_latest:传输带宽 (rs.delivery_rate) 的最大往返 1 次。

BBR.inflight_latest:已传输数据量 (rs.delivered) 的 1 次往返最大值。

在每轮结束时遇到新标记的损失的 ACK 时,流将更新其模型(bw_lo 和 inflight_lo),如下所示:

      bw_lo     = max(       bw_latest, BBRBeta *       bw_lo )
inflight_lo     = max( inflight_latest, BBRBeta * inflight_lo )

此逻辑可以表示为:

  /* Near start of ACK processing: */
  BBRUpdateLatestDeliverySignals():
    BBR.loss_round_start = 0
    BBR.bw_latest       = max(BBR.bw_latest,       rs.delivery_rate)
    BBR.inflight_latest = max(BBR.inflight_latest, rs.delivered)
    if (rs.prior_delivered >= BBR.loss_round_delivered)
      BBR.loss_round_delivered = C.delivered
      BBR.loss_round_start = 1

  /* Near end of ACK processing: */
  BBRAdvanceLatestDeliverySignals():
    if (BBR.loss_round_start)
      BBR.bw_latest       = rs.delivery_rate
      BBR.inflight_latest = rs.delivered

  BBRResetCongestionSignals():
    BBR.loss_in_round = 0
    BBR.bw_latest = 0
    BBR.inflight_latest = 0

  /* Update congestion state on every ACK */
  BBRUpdateCongestionSignals():
    BBRUpdateMaxBw()
    if (rs.losses > 0)
      BBR.loss_in_round = 1
    if (!BBR.loss_round_start)
      return  /* wait until end of round trip */
    BBRAdaptLowerBoundsFromCongestion()
    BBR.loss_in_round = 0

  /* Once per round-trip respond to congestion */
  BBRAdaptLowerBoundsFromCongestion():
    if (BBRIsProbingBW())
      return
    if (BBR.loss_in_round())
      BBRInitLowerBounds()
      BBRLossLowerBounds()

  /* Handle the first congestion episode in this cycle */
  BBRInitLowerBounds():
    if (BBR.bw_lo == Infinity)
      BBR.bw_lo = BBR.max_bw
    if (BBR.inflight_lo == Infinity)
      BBR.inflight_lo = cwnd

  /* Adjust model once per round based on loss */
  BBRLossLowerBounds()
    BBR.bw_lo       = max(BBR.bw_latest,
                          BBRBeta * BBR.bw_lo)
    BBR.inflight_lo = max(BBR.inflight_latest,
                          BBRBeta * BBR.infligh_lo)

  BBRResetLowerBounds():
    BBR.bw_lo       = Infinity
    BBR.inflight_lo = Infinity

  BBRBoundBWForModel():
    BBR.bw = min(BBR.max_bw, BBR.bw_lo, BBR.bw_hi)

4.6. 更新控制参数

BBR 使用三个不同但相互关联的控制参数:起搏率、发送量程和拥塞窗口 (CWND)。

4.6.1. 状态机中的控制行为摘要

下表总结了 BBR 如何在每种状态下调节控制参数。在下表中,列的语义如下:

  • 状态:BBR 状态机中的状态,如上面的“状态转换图”部分所述。
  • 战术:从上面的“状态机战术”小节中选择的战术:“accel”表示加速,“decel”表示减速,“cruise”表示巡航。
  • Pacing Gain:在给定状态下用于BBR.pacing_gain的值。
  • Cwnd Gain:在给定状态下用于BBR.cwnd_gain的值。
  • Rate Cap:作为边界应用于计算 BBR.bw 的 BBR.max_bw 值的速率值。
  • Volume Cap:作为边界应用于 BBR.max_inflight 值以计算 cwnd 的体积值。

控制行为可以总结如下。在处理每个 ACK 时,BBR 使用下表中的值来计算 BBRBoundBWForModel() 中的 BBR.bw,以及 BBRBoundCwndForModel() 中的 cwnd:

+-----------------+--------+--------+------+--------+------------------+
| State           | Tactic | Pacing | Cwnd | Rate   | Volume           |
|                 |        | Gain   | Gain | Cap    | Cap              |
+-----------------+--------+--------+------+--------+------------------+
| Startup         | accel  | 2.77   | 2    |        |                  |
|                 |        |        |      |        |                  |
+-----------------+--------+--------+------+--------+------------------+
| Drain           | decel  | 0.5    | 2    | bw_hi, | inflight_hi,     |
|                 |        |        |      | bw_lo  | inflight_lo      |
+-----------------+--------+--------+------+--------+------------------+
| ProbeBW_DOWN    | decel  | 0.9    | 2    | bw_hi, | inflight_hi,     |
|                 |        |        |      | bw_lo  | inflight_lo      |
+-----------------+--------+--------+------+--------+------------------+
| ProbeBW_CRUISE  | cruise | 1.0    | 2    | bw_hi, | 0.85*inflight_hi |
|                 |        |        |      | bw_lo  | inflight_lo      |
+-----------------+--------+--------+------+--------+------------------+
| ProbeBW_REFILL  | accel  | 1.0    | 2    | bw_hi  | inflight_hi      |
|                 |        |        |      |        |                  |
+-----------------+--------+--------+------+--------+------------------+
| ProbeBW_UP      | accel  | 1.25   | 2    | bw_hi  | inflight_hi      |
|                 |        |        |      |        |                  |
+-----------------+--------+--------+------+--------+------------------+
| ProbeRTT        | decel  | 1.0    | 0.5  | bw_hi, | 0.85*inflight_hi |
|                 |        |        |      | bw_lo  | inflight_lo      |
+-----------------+--------+--------+------+--------+------------------+
4.6.2. 起搏率:BBR.pacing_rate

为了帮助将数据包到达速率与流可用的瓶颈带宽相匹配,BBR 会调整数据包的速度。步调强制执行 BBR 计划传输数据包量程的最大速率。

发送主机通过在每个数据包计划离开时保持量子间间隔来实现起搏,计算给定流 (BBR.next_departure_time) 的数据包的下一次离开时间作为最新数据包大小和当前起搏率的函数,如下所示:

  BBR.next_departure_time = max(Now(), BBR.next_departure_time)
  packet.departure_time = BBR.next_departure_time
  pacing_delay = packet.size / BBR.pacing_rate
  BBR.next_departure_time = BBR.next_departure_time + pacing_delay

为了适应瓶颈,通常 BBR 将起搏率设置为与 bw 成正比,并具有动态增益或比例比例因子,称为 pacing_gain。

当 BBR 流开始时,它没有 bw 估计值(bw 为 0)。因此,在本例中,它根据传输发送方实现的初始拥塞窗口(“InitialCwnd”,例如来自 [RFC6928])、第一个非零 RTT 样本后的初始 SRTT(平滑往返时间)以及初始pacing_gain设置初始步调速率:

  BBRInitPacingRate():
    nominal_bandwidth = InitialCwnd / (SRTT ? SRTT : 1ms)
    BBR.pacing_rate =  BBRStartupPacingGain * nominal_bandwidth

初始化后,只要它估计已填充管道(BBR.filled_pipe为真;有关详细信息,请参阅“启动”部分),或者这样做会增加起搏率,就可以在每个数据上 ACK BBR 更新其起搏率,使其与 bw 成正比。以这种方式限制起搏率更新有助于连接可靠地探测带宽,直到它估计它已达到其全部可用带宽(“填满管道”)。特别是,这可以防止在连接仅看到应用程序受限的带宽样本时降低起搏率。BBR 通过执行 BBRSetPacingRate() 步骤来更新每个 ACK 的起搏率,如下所示:

  BBRSetPacingRateWithGain(pacing_gain):
    rate = pacing_gain * bw * (100 - BBRPacingMarginPercent) / 100
    if (BBR.filled_pipe || rate > BBR.pacing_rate)
      BBR.pacing_rate = rate

  BBRSetPacingRate():
    BBRSetPacingRateWithGain(BBR.pacing_gain)

为了帮助推动网络实现更低的队列和更低的延迟,同时保持高利用率,BBRPacingMarginPercent 常数 1 旨在使 BBR 的平均速度低于 bw 1%。

4.6.3. 发送量程:BBR.send_quantum

为了分摊发送过程中涉及的每个数据包开销(主机 CPU、NIC 处理和中断处理延迟),高性能传输发送方实现(例如 Linux TCP)通常会将包含多个数据包(多个 SMSS)值的聚合调度为单个量程(使用 TSO、GSO 或其他卸载机制)。BBR 拥塞控制算法明确地做出此控制决策,动态计算指定这些传输聚合的最大大小的量子控制参数。此决定基于权衡:

  • 在较低的数据速率下,较小的量子是首选,因为它会导致更短的数据包突发、更短的队列、更低的排队延迟和更低的数据包丢失率。
  • 在更高的数据速率下可能需要更大的量子,因为它可以降低发送和接收主机的 CPU 开销,这些主机可以通过网络堆栈单次传输大量数据。

在每次 ACK 上,BBR 运行 BBRSetSendQuantum() 以更新BBR.send_quantum,如下所示:

  BBRSetSendQuantum():
    if (BBR.pacing_rate < 1.2 Mbps)
      floor = 1 * SMSS
    else
      floor = 2 * SMSS
    BBR.send_quantum = min(BBR.pacing_rate * 1ms, 64KBytes)
    BBR.send_quantum = max(BBR.send_quantum, floor)

BBR 实现可以使用替代方法来选择BBR.send_quantum,具体取决于发送方和接收方预期的 CPU 开销以及网络路径中预期的缓冲注意事项。但是,为了网络和其他用户,BBR 实现应尝试使用最小的可行量子。

4.6.4. 拥塞窗口

拥塞窗口 (cwnd) 控制 BBR 在任何时候允许在网络中传输的最大数据量。它是算法估计的最大飞行数据量,适用于匹配当前网络路径传递过程,给定模型中的所有可用信号,在任何时间尺度上。BBR 根据其网络路径模型和状态机关于如何探测该路径的决策来调整 cwnd。

默认情况下,BBR 会增大其 cwnd 以满足其 BBR.max_inflight,该 _inflight 对实现完全吞吐量所需的内容进行建模,因此会进行缩放以适应从其路径模型计算出的估计 BDP。但是,BBR 选择 cwnd 是为了在动态适应各种条件的竞争考虑因素之间明确权衡。因此,在损失恢复中,BBR 会根据最近的交付样本更保守地调整其发送行为,如果 BBR 需要重新探测路径的当前BBR.min_rtt,则它会相应地削减其 cwnd。以下各节介绍影响 cwnd 的各种注意事项。

4.6.4.1. 初始 cwnd

BBR 通常使用测量来构建网络路径模型,然后根据该模型将控制决策调整到路径。因此,初始 cwnd 的选择被认为超出了 BBR 算法的范围,因为在初始化时,还没有 BBR 可以操作的测量值。因此,在初始化时,BBR 使用传输发送方实现的初始拥塞窗口(例如,TCP 的 [RFC6298] 除外)。

4.6.4.2. 计算 BBR.max_inflight

BBR BBR.max_inflight 是 BBR 允许在飞行中传输的数据量的上限。这个界限始终存在,并且在所有其他考虑因素都得到满足时占主导地位:流不在损失恢复中,不需要探测BBR.min_rtt,并且通过接收足够的 ACK 逐渐增加当前 cwnd 以满足 BBR.max_inflight,从而积累了对其模型参数的置信度。

在每个 ACK 上,BBR 计算 BBRUpdateMaxInflight() 中的 BBR.max_inflight,如下所示:

  BBRBDPMultiple(gain):
    if (BBR.min_rtt == Inf)
      return InitialCwnd /* no valid RTT samples yet */
    BBR.bdp = BBR.bw * BBR.min_rtt
    return gain * BBR.bdp

  BBRQuantizationBudget(inflight)
    BBRUpdateOffloadBudget()
    inflight = max(inflight, BBR.offload_budget)
    inflight = max(inflight, BBRMinPipeCwnd)
      if (BBR.state == ProbeBW && BBR.cycle_idx == ProbeBW_UP)
      inflight += 2
    return inflight

  BBRInflight(gain):
    inflight = BBRBDPMultiple(gain)
    return BBRQuantizationBudget(inflight)

  BBRUpdateMaxInflight():
    BBRUpdateAggregationBudget()
    inflight = BBRBDPMultiple(BBR.cwnd_gain)
    inflight += BBR.extra_acked
    BBR.max_inflight = BBRQuantizationBudget(inflight)

“estimated_bdp”项试图通过允许流在 BBR.bw 的持续时间内以 BBR.min_rtt 的速度发送,从而允许足够的数据包在传输中充分利用路径的估计 BDP。通过BBR.cwnd_gain将飞行中数据扩展到 BDP 的小倍数,以处理常见的网络和接收器行为,例如延迟、拉伸或聚合 ACK[A15]。“量程”术语允许在发送和接收主机上飞行的足够量程达到高吞吐量,即使在使用卸载机制的环境中也是如此。

4.6.4.3. 流水线的最小cwnd

对于 BBR.max_inflight,BBR 施加了 BBRMinPipeCwnd(4 个数据包,即 4 * SMSS)的下限。此下限有助于确保即使在非常低的 BDP 下,并且在像 TCP 这样的传输中,接收方只能确认每个备用 SMSS 的数据,也有足够的数据包在传输中保持完整的流水线。具体而言,BBR 尝试允许至少 2 个数据包在传输中,并且 ACK 在从接收方到发送方的路径上允许至少 2 个数据包。

4.6.4.4. 在损失恢复中调节cwnd

BBR 将损失解释为一种暗示,即路径行为可能存在最近的变化,但这些变化尚未完全反映在其路径模型中,因此它需要更加保守。

在重传超时 (RTO) 时,BBR 会保守地将 cwnd 减小到允许传输 1 条短信的值。然后,BBR 使用下面“核心 cwnd 调整机制”中概述的正常方法逐渐增加 cwnd。

当 BBR 发送方检测到数据包丢失,但仍有数据包在传输中时,在第一轮丢失修复过程中,BBR 会暂时降低 cwnd 以匹配 ACK 到达时的当前传输速率。在第二轮及以后的损失修复中,它确保发送速率在 ACK 到达时永远不会超过当前交付率的两倍。

当 BBR 退出损失恢复时,它会将 cwnd 恢复到 cwnd 在进入恢复之前保持的“最后已知商品”值。无论流退出损失恢复是因为它完成了所有损失的修复,还是在推断损失恢复事件是虚假的后执行了“撤消”事件,这同样适用。

有几种方法可以实现此高级设计,以便在损失恢复中更新 cwnd。一是如下:

重新传输超时 (RTO) 时:

  BBROnEnterRTO():
    BBR.prior_cwnd = BBRSaveCwnd()
    cwnd = packets_in_flight + 1

进入快速恢复后,将 cwnd 设置为仍在传输中的数据包数(允许至少一个数据包进行快速重传):

  BBROnEnterFastRecovery():
    BBR.prior_cwnd = BBRSaveCwnd()
    cwnd = packets_in_flight + max(rs.newly_acked, 1)
    BBR.packet_conservation = true

在快速恢复中的每个 ACK 上,运行以下 BBRModulateCwndForRecovery() 步骤,这有助于确保在第一轮恢复时保存数据包,并在后续几轮恢复中以不超过当前传递速率的两倍发送(假设“rs.newly_acked”数据包被新标记为 ACK 或 SACKed,而“rs.newly_lost”是新标记为丢失的):

  BBRModulateCwndForRecovery():
    if (rs.newly_lost > 0)
      cwnd = max(cwnd - rs.newly_lost, 1)
    if (BBR.packet_conservation)
      cwnd = max(cwnd, packets_in_flight + rs.newly_acked)

在快速恢复中往返一次后:

  BBR.packet_conservation = false

在退出损失恢复(RTO 恢复或快速恢复)时,无论是通过修复所有损失还是撤消恢复,BBR 都会恢复我们在进入损失恢复时所拥有的最知名的 cwnd 值:

  BBR.packet_conservation = false
  BBRRestoreCwnd()

请注意,退出损失恢复发生在 ACK 处理过程中,在 ACK 处理结束时,BBRBoundCwndForModel() 将根据当前模型参数绑定 cwnd。因此,损失恢复后的 cwnd 和起搏率通常小于进入损失恢复的值。

BBRSaveCwnd() 和 BBRRestoreCwnd() 帮助程序可帮助记住和恢复最后已知的良好 cwnd(未通过丢失恢复或 ProbeRTT 调制的最新 cwnd),定义如下:

  BBRSaveCwnd():
    if (!InLossRecovery() and BBR.state != ProbeRTT)
      return cwnd
    else
      return max(BBR.prior_cwnd, cwnd)

  BBRRestoreCwnd():
    cwnd = max(cwnd, BBR.prior_cwnd)
4.6.4.5. 在ProbeRTT中调制cwnd

如果 BBR 决定需要进入 ProbeRTT 状态(请参阅下面的“ProbeRTT”部分),其目标是快速减少飞行中的数据量并耗尽瓶颈队列,从而允许测量BBR.min_rtt。为了实现此模式,BBR 将 cwnd 绑定到 BBRMinPipeCwnd,这是允许流水线的最小值(请参阅上面的“流水线的最小 cwnd”部分):

  BBRProbeRTTCwnd():
    probe_rtt_cwnd = BBRBDPMultiple(BBR.bw, BBRProbeRTTCwndGain)
    probe_rtt_cwnd = max(probe_rtt_cwnd, BBRMinPipeCwnd)
    return probe_rtt_cwnd

  BBRBoundCwndForProbeRTT():
    if (BBR.state == ProbeRTT)
      cwnd = min(cwnd, BBRProbeRTTCwnd())
4.6.4.6. 核心CWND调整机制

通过它的网络路径和流量可能会突然发生巨大变化。为了平稳、稳健地适应这些变化,并减少在这种情况下的数据包丢失,BBR 采用了保守的策略。当 cwnd 高于从 BBR 的路径模型派生的 BBR.max_inflight 时,BBR 会立即将 cwnd 切入 BBR.max_inflight。当 cwnd 低于 BBR.max_inflight 时,BBR 会逐渐且谨慎地提高 cwnd,增加 cwnd 的次数不超过每次 ACK 上确认的数据量(累积或选择性)。

具体来说,在将“rs.newly_acked”数据包确认为新 ACK 或 SACKed 的每个 ACK 上,BBR 运行以下 BBRSetCwnd() 步骤来更新 cwnd:

  BBRSetCwnd():
    BBRUpdateMaxInflight()
    BBRModulateCwndForRecovery()
    if (!BBR.packet_conservation) {
      if (BBR.filled_pipe)
        cwnd = min(cwnd + rs.newly_acked, BBR.max_inflight)
      else if (cwnd < BBR.max_inflight || C.delivered < InitialCwnd)
        cwnd = cwnd + rs.newly_acked
      cwnd = max(cwnd, BBRMinPipeCwnd)
    }
    BBRBoundCwndForProbeRTT()
    BBRBoundCwndForModel()

上述逻辑中体现了几个考虑因素。如果 BBR 已测量了足够的样本,以确信它已填充管道(请参阅下面“启动”部分中对 BBR.filled_pipe的描述),则它会根据传递的数据包数量增加其 cwnd,同时限制其 cwnd 不大于适应估计 BDP 的 BBR.max_inflight。否则,如果 cwnd 低于 BBR.max_inflight,或者发送方标记的传递数据太少(小于 InitialCwnd),以至于它尚未判断其 BBR.max_bw 估计值和 BBR.max_inflight 是否有用,则它会增加 cwnd,而不会将其限制为低于 BBR.max_inflight。最后,BBR 施加了 BBRMinPipeCwnd 的下限,以便即使使用较小的 BDP,也能允许流水线(请参阅上面的“流水线的最小 cwnd”部分)。

4.6.4.7. 基于近期拥塞的边界cwnd

最后,BBR 根据最近的拥塞情况对 cwnd 进行边界,如表的“状态机控制行为摘要”部分的“音量上限”列所述:

  BBRBoundCwndForModel():
    cap = Infinity
    if (IsInAProbeBWState() and
        BBR.state != ProbeBW_CRUISE)
      cap = BBR.inflight_hi
    else if (BBR.state == ProbeRTT or
             BBR.state == ProbeBW_CRUISE)
      cap = BBRInflightWithHeadroom()

    /* apply inflight_lo (possibly infinite): */
    cap = min(cap, BBR.inflight_lo)
    cap = max(cap, BBRMinPipeCwnd)
    cwnd = min(cwnd, cap)

五、落实情况

本部分记录了在发布本互联网草案时本规范定义的算法的已知实现的状态,并基于 [RFC7942] 中描述的提案。本节中的实现描述旨在帮助 IETF 在其决策过程中将草案推进到 RFC。请注意,此处列出的任何单个实现并不意味着 IETF 的认可。此外,没有花费任何精力来验证此处提供的信息,这些信息是由 IETF 贡献者提供的。这并不是要作为,也不得被解释为可用实现或其功能的目录。建议读者注意,可能存在其他实现。

根据 [RFC7942],“这将使审查者和工作组能够对具有运行代码优势的文档进行适当考虑,这些文档可以作为有价值的实验和反馈的证据,这些实验和反馈使实现的协议更加成熟。各工作组应根据其认为适当的方式使用这些信息”。

截至撰写本文时,BBR 的以下实现已公开发布:

  • Linux TCP协议

    • 源代码URL:

      • https://github.com/google/bbr/blob/v2alpha/README.md
      • https://github.com/google/bbr/blob/v2alpha/net/ipv4/tcp_bbr2.c
    • 资料来源:谷歌
    • 成熟度:生产
    • 许可证:双重许可:GPLv2 / BSD
    • 联系人:https://groups.google.com/d/forum/bbr-dev
    • 最后更新:2021年8月21日
  • 奎克

    • 源代码 URL:

      • https://cs.chromium.org/chromium/src/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc
      • https://cs.chromium.org/chromium/src/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.h
    • 资料来源:谷歌
    • 成熟度:生产
    • 许可证:BSD风格
    • 联系人:https://groups.google.com/d/forum/bbr-dev
    • 最后更新:2021年10月21日

6. 安全注意事项

此提案不会对传输协议或拥塞控制算法的底层安全性进行任何更改。BBR 与现有的标准拥塞控制算法 [RFC5681] 具有相同的安全考虑因素。

7. IANA 注意事项

本文件不涉及任何 IANA 行动。在这里,我们使用了 [RFC5226] 建议的短语,因为 BBR 不会修改或扩展任何网络协议的线路格式,也不会对分配的号码添加新的依赖性。BBR 仅涉及对传输发送方的拥塞控制算法的更改,而不涉及网络、接收方或任何网络协议的更改。

RFC 编辑注意:此部分可能会在作为 RFC 发布时删除。

8. 致谢

作者感谢 Len Kleinrock 在拥堵控制理论方面的工作。我们感谢 Larry Brakmo 在 Vegas [BP95] 和 New Vegas [B15] 拥堵控制算法方面的开创性工作,这些算法预示着 BBR 的许多元素,并感谢 Larry 在 BBR 早期开发期间的建议和指导。作者还要感谢 Kevin Yang、Priyaranjan Jha、Yousuk Seung、Luke Hsiao 在 TCP BBR 方面所做的工作;Jana Iyengar、Victor Vasiliev 和 Bin Wu 在 QUIC BBR 方面的工作;以及 Matt Mathis 对 BBR 算法及其影响的研究工作 [MM19]。我们还要感谢 C. Stephen Gunn、Eric Dumazet、Nandita Dukkipati、Pawel Jurczyk、Biren Roy、David Wetherall、Amin Vahdat、Leonidas Kontothanassis 以及 YouTube、google.com、Bandwidth Enforcer 和 Google SRE 团队的宝贵帮助和支持。感谢 Randall R. Stewart、Jim Warner、Loganaden Velvindron、Hiren Panchasara 和 Adrian Zapletal 对本文档早期版本的反馈和建议。

9. 参考资料

9.1. 规范性引用

[RFC793]

Postel, J.,“传输控制协议”,1981 年 9 月。

[RFC2018]

马蒂斯,M.J. Mahdavi,“TCP 选择性确认选项”,RFC 2018,1996 年 10 月,<http://www.rfc-editor.org/rfc/rfc2018.txt>。

[RFC7323]

Borman, D.、Braden, B.、Jacobson, V. 和 R. Scheffenegger,“高性能的 TCP 扩展”,2014 年 9 月。

[RFC2119]

Bradner, S.,“RFC 中用于指示需求级别的关键词”,RFC 2119,1997 年 3 月,<http://www.rfc-editor.org/rfc/rfc2119.txt>。

[RFC5226]

纳尔滕,T.H. Alvestrand,“在 RFC 中编写 IANA 注意事项部分的指南”,2008 年 5 月。

[RFC6298]

Paxson, V.,“计算 TCP 的重传计时器”,RFC 6298,2011 年 6 月,<Home | Internet-Draft Author Resources>。

[RFC5681]

Allman, M.、Paxson, V. 和 E. Blanton,“TCP 拥塞控制”,RFC 5681,2009 年 9 月,<https://tools.ietf.org/html/rfc5681>。

[RFC7942]

谢弗,Y.A. Farrel,“提高运行代码的意识:实现状态部分”,2016 年 7 月。

[RFC8312]

Rhee, I., Xu, L., Ha, S., Zimmermann, A., Eggert, L., and R. Scheffenegger, “CUBIC for Fast Long-Distance Networks”, 2018年2月, <RFC 8312 - CUBIC for Fast Long-Distance Networks>.

[RFC8985]

Cheng, Y.、Cardwell, N.、Dukkipati, N. 和 P. Jha,“TCP 的 RACK-TLP 损失检测算法”,RFC 8985,DOI 10.17487/RFC8985,2021 年 2 月,<https://www.rfc-editor.org/info/rfc8985>。

[RFC9000]

艾扬格,J.,编辑。和 M. Thomson, Ed.,“QUIC:A UDP-BASED MULTIPLEXED AND SECURE TRANSPORT”,RFC 9000,DOI 10.17487/RFC9000,2021 年 5 月,<https://www.rfc-editor.org/info/rfc9000>。

[RFC4340]

Kohler, E.、Handley, M. 和 S. Floyd,“数据报拥塞控制协议 (DCCP)”,RFC 4340,DOI 10.17487/RFC4340,2006 年 3 月,<https://www.rfc-editor.org/info/rfc4340>。

9.2. 信息性参考资料

[draft-cheng-iccrg-delivery-rate-estimation]

Cheng, Y.、Cardwell, N.、Hassas Yeganeh, S. 和 V. Jacobson,“交付率估计”,正在进行中,互联网草案,draft-cheng-iccrg-delivery-rate-estimatemation,2021 年 11 月,<https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation>。

[CCGHJ16]

Cardwell, N.、Cheng, Y.、Gunn, C.、Hassas Yeganeh, S. 和 V. Jacobson,“BBR:基于拥塞的拥塞控制”,ACM 队列 2016 年 10 月,2016 年 10 月,<BBR: Congestion-Based Congestion Control - ACM Queue>。

[CCGHJ17]

Cardwell, N.、Cheng, Y.、Gunn, C.、Hassas Yeganeh, S. 和 V. Jacobson,“BBR:基于拥堵的拥堵控制”,ACM 通讯,2017 年 2 月,2017 年 2 月,<https://cacm.acm.org/magazines/2017/2/212428-bbr-congestion-based-congestion-control/pdf>。

[MM19系列]

马蒂斯,M.J. Mahdavi,“弃用 TCP 宏观模型”,《计算机通信评论》,第 49 卷,第 5 期,第 63-68 页,2019 年 10 月。

[BBRStartupPacing增益]

Cardwell, N.、Cheng, Y.、Hassas Yeganeh, S. 和 V. Jacobson,“BBR 创业起搏增益:推导”,2018 年 6 月,<https://github.com/google/bbr/blob/master/Documentation/startup/gain/analysis/bbr_startup_gain.pdf>。

[BBRDrainPacing增益]

Cardwell, N.、Cheng, Y.、Hassas Yeganeh, S. 和 V. Jacobson,“BBR 漏极起搏增益:推导”,2021 年 9 月,<https://github.com/google/bbr/blob/master/Documentation/startup/gain/analysis/bbr_drain_gain.pdf>。

[草案-romo-iccrg-ccid5]

Romo, N.、Kim, J. 和 M. Amend,“数据报拥塞控制协议 (DCCP) 拥塞控制 ID 5 配置文件”,正在进行中,Internet-Draft,draft-romo-iccrg-ccid5,2021 年 10 月 25 日,<draft-romo-iccrg-ccid5-00>。

[DC13号]

杜马泽特,E.和 Y. Cheng,“TSO、公平排队、节奏:三的魅力”,IETF 88,2013 年 11 月,<https://www.ietf.org/proceedings/88/slides/slides-88-tcpm-9.pdf>。

[答15]

Abrahamsson, M.,“TCP ACK 抑制”,IETF AQM 邮件列表,2015 年 11 月,<[aqm] TCP ACK Suppression>。

[雅克88]

Jacobson, V.,“拥塞避免和控制”,SIGCOMM 1988,Computer Communication Review,第 18 卷,第 4 期,第 314-329 页,1988 年 8 月,<http://ee.lbl.gov/papers/congavoid.pdf>。

[江淮90]

Jacobson, V., “Modified TCP Congestion Avoidance Algorithm”, end2end-interest mailing list , April 1990, <ftp://ftp.isi.edu/end2end/end2end-interest-1990.mail>.

[BP95号]

布拉克莫,L.L. Peterson,“TCP Vegas: End-to-end congestion avoidance on a global Internet”,IEEE Journal on Selected Areas in Communications 13(8):1465-1480,1995 年 10 月。

[B15层]

Brakmo, L.,“TCP-NV:TCP-Vegas的更新”,2015年8月,<https://docs.google.com/document/d/1o-53jbO_xH-m9g2YCgjaf5bK8vePjWP6Mk0rYiRLK-U/edit>。

[WS95系列]

赖特,G.W. Stevens, “TCP/IP Illustrated, Volume 2: The Implementation”, Addison-Wesley , 1995.

[HRX08系列]

Ha, S.、Rhee, I. 和 L. Xu,“CUBIC:一种新的 TCP 友好型高速 TCP 变体”,ACM SIGOPS 操作系统评论,2008 年。

[GK81系列]

盖尔,R.L. Kleinrock,“计算机网络电源的不变属性”,国际通信会议论文集,1981 年 6 月,<http://www.lk.cs.ucla.edu/data/files/Gail/power.pdf>。

[K79系列]

Kleinrock, L.,“计算机通信中概率问题的功率和确定性经验法则”,1979 年国际通信会议论文集。

作者地址

Neal Cardwell
Google
Yuchung Cheng
Google
Soheil Hassas Yeganeh
Google
Ian Swett
Google
Van Jacobson
Google
Email: 
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值