# TCP Cubic 准备起飞

* 原文链接：[TCP Cubic is Ready to Take Flight](https://freebsdfoundation.org/wp-content/uploads/2021/05/TCP-Cubic-is-Ready-to-Take-Flight.pdf)
* 作者：**RICHARD SCHEFFENEGGER**

随着 FreeBSD 13.0 的发布，cc\_cubic 可加载拥塞控制模块进行了许多改进。TCP Cubic 最初由 Lawrence Stewart 在他于斯威本科技大学高级互联网架构中心工作期间实现，基于最终成为 RFC8312 的早期草案。如今，TCP Cubic 已成为当前使用的事实标准拥塞控制机制。

## TCP Cubic

FreeBSD 长期以来使用的默认 TCP 拥塞控制算法是 NewReno，它是 Reno 拥塞控制机制的一个变种，具有改进的丢包恢复功能。拥塞控制算法的工作是检测并防止网络过载情况，即传输的数据超过了网络能够承载或交付的能力。NewReno 曾经是这一领域的黄金标准，但它也存在一些限制。

尽管 Van Jacobson 已经证明，任何 AIMD（加性增加，乘性减少）方案在控制流量时都能表现出稳定的操作，但在现代高速链路上，NewReno 提高有效传输速度所需的时间并不理想。如果检测到过载情况——通常使用显式信号（如数据包丢失或 TCP/IP 头中的特定标志位）——NewReno 会将有效传输速度降低至过载发生时的一半。若有足够的传输数据，并且本地应用程序以足够高的速度提供，NewReno 将大约每个往返时间（RTT）增加一个完整数据包的传输速度。

然而，使用现代网络技术（例如，跨越国家的 10G 链路，延迟为 100 毫秒）运行这些数据时，一个单独的 NewReno 会话可能需要（5 Gbps / (1500 *8))* 0.1 秒 RTT ≈ 10 小时，才能重新利用所有可用带宽——前提是没有其他数据包丢失（作为拥塞的指示）。

![](https://github.com/user-attachments/assets/b7d8d2bd-f1d4-4ecc-a515-ac0f4ae8fdad)

* 脆弱的丢包响应，无法扩展的增长
* 无法扩展的线性增长：\
  需要 1000 倍的时间才能达到 1000 倍的带宽
* 要完全利用一个 10G、100 毫秒的链路，需要在丢包之间保持 >1 小时\
  丢包率 < 0.0000000002 (< 2•10⁻¹⁰)

  当 TCP 在发送无限量的数据时，设计上是为了探测并最终超越网络的最大带宽，然而缓慢的增长会对这个目标产生不利影响。

TCP Cubic 通过两个主要的变化来解决这些限制。第一个变化是在过载时将传输速度仅降低到 70%（在早期草案中为 80%）。第二个变化是在之后使用立方函数进行增长，该函数的缩放方式使其能够在很长一段时间内徘徊在上一个限制值附近，但快速地达到该限制值——如果网络的可用带宽不再受限，则能够越来越快地增长，有效地匹配 TCP 慢启动期间的指数带宽增长。

![](https://github.com/user-attachments/assets/c6ac473a-a506-4f60-9f41-26ef5bba4f1a)

* 按照立方（x³）函数增长：需要 10 倍的时间才能达到 1000 倍的更高带宽 -> 100 倍更具攻击性
* 要完全利用一个 10G、100 毫秒的链路，需要在丢包之间保持 >40 秒\
  丢包率 < 0.0000000003 (< 3•10⁻⁸)
* 更高的平均带宽和更快速的增长可能暴露出网络中的潜在问题
  * 如果报告性能下降，请监控重传和重传超时。
* 大多数环境将看到更好的性能。

虽然所有这些基础都已实现——包括用于计算立方根的快速整数近似——但一些参数在 2007 年的立方草案和最终的 RFC8312 之间发生了变化。因此，需要进行一些工作以使现有代码符合 RFC 的要求。

与此同时，大多数其他主要操作系统已经将 Cubic 作为默认的拥塞控制机制，因为在 NewReno 和 Cubic 之间的直接竞争中，使用 NewReno 的流会占用较少的带宽份额。幸运的是，Cubic 的设计方式并不会完全让其他拥塞控制机制陷入饥饿状态。

现有的代码也假设了立方代码中的一些隐式限制，这些限制在通用流量模式下并不总是成立。一些边缘情况没有得到充分解决。例如，现在应用程序限制的会话已成为常态。这是指当 TCP 基本上没有数据可发送时，所有由处理更多数据驱动的状态引擎在时间上发生中断。由于 Cubic 使用挂钟时间而不是会话中数据的传输时间，这——而不是 D23655（立方和插槽启动交互）——慢启动……这造成了一些不良的影响。（作者——这个变化是正确的吗，还是我们误解了？）

在开始将 Cubic 作为通用拥塞控制在 FreeBSD 上运行时，以下问题已得到解决，尽管没有声称这是一个完整的列表。在处理 Cubic 的过程中，还发现并修复了一些与 TCP 基础栈相关的一般问题。

* D26181（编辑修正）
* D26060（持续调整 cwnd，而不仅仅是每个窗口调整一次——导致大量流量突发）
* D25976（将 ECN 视为与 Cubic 的数据包丢失）
* D25746（正确地计时 Cubic 纪元的开始与慢启动）
* D25133（Cubic 和 RTO 的交互）
* D25065（Cubic 和应用程序限制）
* D24657（编辑修正）
* D23655（Cubic 和插槽启动交互）
* D23353（Cubic 和 ECN）
* D19118（处理 Cubic 数学中的溢出）
* D18982（为良好的 Cubic 数学做准备）
* D18954（Cubic 和空闲后启动）

总的来说，自 FreeBSD 8.0 以来可用的 Cubic 基础功能和算法一直是一个坚实的基础。由于缺乏生产环境部署，一些边缘案例和边界条件——例如非常长时间运行的 TCP 会话——未得到检查。

通过上述改进，FreeBSD 13.0 中的 TCP Cubic 变体应该能够提供略好的吞吐量，特别是在高延迟的公共互联网会话中。然而，尽管代码现在已经处于更为稳健的状态，可以处理大多数场景，但仍然可能会出现一些缺陷，尤其是在面临特殊流量模式时。

不仅 Lawrence Stewart 对此次改进工作非常有帮助，许多繁重的工作也由 Cheng Cui 完成，特别是在回归和单元测试方面，以及发现所有这些边缘案例并提供代码改进。此外，在每两周一次的 FreeBSD 传输组电话会议上也进行了许多富有成效的讨论。

***

**RICHARD SCHEFFENEGGER** 是 NetApp 的咨询解决方案架构师。
