单流场景下:算法该不该“踩刹车”?

聊拥塞控制,大家默认都在聊多流竞争:几十条流挤一根管子,谁多谁少,公不公平。这没问题,因为互联网本来就是共享的。

但还有一个场景很常见,却总被当成“默认不用管”:单流

你从网盘拖一个 10GB 的文件。你家宽带凌晨只有你一个人在跑 PT。你的专用服务器向上游推数据。这时候,整条链路上只有你这一条 TCP 流。没有邻居抢带宽,没有其他连接制造抖动,队列基本是空的。

这时候你希望算法怎么表现?
直觉就是:有多少带宽,跑多少带宽。别搞那些有的没的。

但不少算法的实际行为恰恰相反。它们带着“时刻准备打仗”的惯性,哪怕战场上只有你自己,也照样做各种“预防动作”。

CUBIC:丢包就砍半

CUBIC 的逻辑很简单:丢包 = 拥塞。在只有一条流的场景,如果遇到一点噪声丢包(比如无线信号波动、浅缓存设备丢了一个包),CUBIC 会认为“堵了”,窗口直接砍半。然后慢慢恢复,再丢,再砍。
带宽可能能跑 1G,结果平均只有 500-800M。不是它不想快,是它设计时就认定丢包一定是拥塞。这个假设在单流场景下经常不成立。

BBR:周期性的“自我刹车”

BBR 比 CUBIC 聪明,不把丢包当成拥塞。但它有自己的周期性动作:每 8 个 RTT 就要来一次排空(DRAIN phase),把发送窗口降到 0.75 倍。这个设计本来是为了多流环境下防止队列堆积,但在单流场景下,它相当于你一个人在高速上开车,每隔几公里自己松一脚油门。
另外,BBR 估计 min_rtt 用的是 10 秒窗口。如果中途路径出现一次短暂抖动(比如路由瞬切、时钟跳变),RTT 变大了一点,那接下来 10 秒它都会用这个偏大的 RTT 算 BDP,窗口被人为缩小。明明没有竞争,却被算法自己憋着。

这些行为都不是“bug”。BBR 的设计前提是“我不确定网络状态,所以我要保守”。这个前提在复杂环境里是合理的,但在单流场景下就变成了过度防御。

KCC 怎么想这件事

KCC 的做法不是推翻 BBR,而是增加一个判断:当前是不是只有我一条流

它通过几个信号去猜:

  • RTT 有没有长期稳定的趋势(没有排队)
  • 飞行中的数据量(inflight)与 BDP 的比值是否一直在 1 附近
  • ACK 到达的间隔是否均匀(没有其他流造成的突发)
  • 丢包是孤立的还是成批的(孤立的大概率是噪声,成批的才是竞争)
  • 估算的带宽是否长时间平稳(没有其他流加入/离开)
  • 应用层还有没有数据要发(免得把“空闲”误判成“无竞争”)

当这些信号都说“没别人”时,KCC 就切到“单流模式”。在这个模式下,它不再做那些为公平性设计的排空动作,允许 inflight 略微超过 BDP(反正队列空的),也不刻意限制增益系数。

简单说:不装模作样了,放开跑

实际跑出来的差别

有人做过对比:香港 VPS 到成都,1G 端口,单条流持续 60 秒。

  • KCC 平均 1.06 Gbps
  • BBR 平均 950 Mbps
  • CUBIC 更低

这 110 Mbps 差距从哪来的?主要就是 BBR 的周期性降速和 min_rtt 采样失准造成的窗口收缩。KCC 在单流模式下绕开了这些。

这不是什么“革命”,就是一个选择

两种思路而已:

思路 A:不管环境怎样,我先假设最坏情况(随时可能堵车),一直保持保守行为。好处是稳,坏处是在好路上也开不快。

思路 B:我先看看路况。没车就跑满,有车再调。好处是能充分利用好路,坏处是多了一套判断逻辑,且判断可能出错。

KCC 选了思路 B。它认为:在单流场景下还踩刹车,等于浪费用户买的带宽。而带宽是花钱买的。

这个想法不复杂,也不需要包装成什么“范式革命”。它就是:你知道没别人,就别自己演了

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐