重读 About VJ 的互联网名人堂报道 Van Jacobson Denies Averting Internet Meltdown in 1980s,作文一篇。
VJ 对旧时 TCP 的崩溃中洞察,提出 self-clock,这是一个创举。
如 VJ 所述,崩溃前的 TCP 运行环境与我们当前的网络有天壤之别,它的规模很小,即便很小它也很慢,人们对它的需求很低,即使什么都不做它也很难崩溃,另一方面,从端到端的视角,虽然当时的 TCP 只依对端通告的窗口发送数据,但这并不是什么问题,因为当时的主机内存同样很小,通告的窗口并不会太大。
在几乎没有任何约束的纯粹尽力而为网络上,1986 年,TCP/IP 崩溃了,VJ 对此进行了思考。
早期的网络实际上是一个受控仲裁网络,类似令牌网,它既复杂又低效,显然这不是设计者的初衷,也因此它的规模无法扩展,利用率不高,反而延迟了问题的暴露,当网络在 1980 年代初过渡到 TCP/IP,起搏于 Unix 和 socket 接口的寒武纪大爆发开始,人们意识到,早期的网络对大规模自组织的依赖会产生意想不到的后果,果不其然,网络很快就拥塞崩溃了。
控制拥塞的手段是控制进入网络的数据包的时间,但 VJ 意识到缺乏一个触发发送的时钟,但他很快意识到,TCP ACK 是一个好的时钟信号,收到一个 ACK 意味着一个数据包离开了网络,意味着可以推入网络一个新数据包,这种对 feedback 的理解看似简单自然但却非常重要。首先,ACK 能返回,意味着网络是畅通的,其次,返回 ACK 的数量度量了网络被腾出的空间。
但紧接着就遭遇到先有鸡先有蛋的问题,ACK 时钟需要发送数据包来触发,而它反过来又触发数据包的发送,一开始到底发送多少呢。
以上就是 VJ 提出慢启动的背景。随后他提出了慢启动,从 1 个包开始启动,收到 1 个 ACK 发送 2 个包,慢启动之 “慢” 指的是起点低,它实际上很快。
慢启动只增加了 1 个变量以及 3 行代码,即完成了 self-clock 的启动(start):the implementation is trivial – one new state variable and three lines of code in the sender:
- Add a congestion window, cwnd, to the perconnection state.
- When starting or restarting after a loss, set cwnd to one packet.
- On each ack for new data, increase cwnd by one packet.
- When sending, send the minimum of the receiver’s advertised window and cwnd.
于是 TCP ACK 作为 self-clock 的闭环控制系统就构建起来了。接下来就是在该控制系统上进行拥塞控制。
self-clock 消除了对本地时钟的依赖,发送方不再需要猜测发送节奏,它只需遵循 ACK 的节奏,就实现发送速率和网络路径的瓶颈带宽和延迟的自动适配。整个 self-clock 控制闭环基于数据包守恒,因此这种负反馈机制使 TCP 能够收敛到一个相对稳定的状态,并温和地共享带宽。
在经典 [VJ88] 之前,还有一个经典 [JRC87] 作为拥塞控制的理论基础。[VJ88] 引用了 [JRC87] 的结论。
[JRC87] 提出膝点和崖点的概念,围绕膝点的控制是拥塞避免,围绕崖点的控制是拥塞控制,取决于 feedback,VJ 本着 “丢包从不撒谎” 的理念,从 Multiplicative Decrease 开始,将状态控制在膝点和崖点之间,这就是经典的 Loss-based AIMD 拥塞控制。
VJ 采用管道守恒建模,在平衡状态,设 L i L_i Li 为网络在时刻 i 的负载,则:
L i = N L_i=N Li=N
其中,N 为常量,表示一个稳定状态下网络的平均负载。
然而一旦发生拥塞, L i L_i Li 至少由两部分组成,N 和上一个时刻的 L i − 1 L_{i-1} Li−1:
L i = N + γ ⋅ L i − 1 L_i=N+\gamma\cdot L_{i-1} Li=N+γ⋅Li−1
该模型的优美之处在于它能描述全貌,我们知道泰勒展开和移动指数平均(或卷积)本身就是按照任意的规定精度描述全貌的,上式子可看作一个包含 2 项的关于 L ( t ) L(t) L(t) 的泰勒展开。从移动指数平均的视角,或者从卷积计算的视角, L i L_i Li 都是指数级增长的:
L i ≈ γ ⋅ L i − 1 L_i\approx \gamma\cdot L_{i-1} Li≈γ⋅Li−1
因此:
L n = γ n L 0 L_n=\gamma^nL_0 Ln=γnL0
为了对冲这种全局负载的指数级增长,则至少需要同等强度的降窗,而 Multiplicative Decrease 则可以让全局负载在拥塞持续期间指数级下降:
W i = d ⋅ W i − 1 W_i=d\cdot W_{i-1} Wi=d⋅Wi−1
这行为在拥塞时一定会被触发,因为 VJ 曾经说过,丢包不会说谎,而拥塞一定会导致 buffer 溢出而丢包,只要捕获到丢包,执行 MD 即可。
虽然拥塞时会丢包,但带宽有盈余时不会有任何事发生,如果不捕获带宽盈余事件,这部分带宽将被浪费掉。按照 AIMD 才可公平且稳定收敛,在没有丢包时,Additive Increase 就是唯一可选:
W i = W i − 1 + u W_i=W_{i-1}+u Wi=Wi−1+u
这就是 [VJ88] 展示的已经运行了 30 年的基于 self-clock 的 AIMD 拥塞控制的背景历程和整体框架,或许还有一些教科书上没有的细节。self-clock 核心之重,它提供了拥塞控制框架的脉搏,让拥塞控制成为一个真正的反馈式控制系统。
自那时起,self-clock 被持续优化了近 30 年,但万变不离其宗。self-clock 之外,RTO 本地定时器被保留了下来,以备 self-clock 丢失后之用,除此之外,SACK,TLP,ER,RACK,以及 Google Swift 硬件时间戳,均为增强 self-clock 而引入,一直到 BBR,self-clock 被用来推断 pacing rate,但本质上还是基于 self-clock 度量的数据包守恒。
但并不是每个人都了解控制系统,你可以实施一些手段,但一定要能获得 feedback,并根据反馈修正手段,self-clock 提供了最根本的 feedback。但多数优化专家并不根据 feedback 来修正手段,甚至忽略 feedback,全是经理。
他们只盯着 AIMD 性能低,却不知道它已经接近最优解了,更不知道优化的代价。诚然,VJ 的假设也不全对,但那只是个时代局限的单点:
On most network paths, loss due to damage is rare (<< 1%) so it is probable that a packet loss is due to congestion in the network. The congestion control scheme we propose is insensitive to damage loss until the loss rate is on the order of one packet per window (e.g., 12-15% for an 8 packet window). At this high loss rate, any window flow control scheme will perform badly–a 12% loss rate degrades TCP throughput by 60%. The additional degradation from the congestion avoidance window shrinking is the least of one’s problems. A presentation in [IETF88] and an in-progress paper address this subject in more detail.
所谓 AIMD 性能问题本质上是对介质的要求,诸如随机丢包(damage)而非拥塞丢包的误判,而不是拥塞控制本身的问题,针对拥塞控制本身的问题,早已有多种长肥管道快速增窗的策略,不再赘述。
拥塞控制是一个全局意义上的统计控制,而不是针对个体的精确控制,但没人能全局控制,恰恰只能针对个体做拥塞控制,然后这些控制 feedback 自发组合成一个全局统计效果,这个过程显然是非线性的复杂系统。
用一个最简化的模型表示个体的演变:
d X d t = k ⋅ ( 1 − p ( t ) ) − μ ⋅ X ( t ) ⋅ p ( t ) \dfrac{dX}{dt}=k\cdot(1-p(t))-\mu\cdot X(t)\cdot p(t) dtdX=k⋅(1−p(t))−μ⋅X(t)⋅p(t)
其中 k 表示增益, μ \mu μ 表示损益,p 表示丢包,整体上看,每一个 X(t) 都是一个典型的随机过程,p 和所有的 X(t) 相互影响,当不再关心特定的锯齿过程,拉远镜头,个体波动被平均掉了,就到了大数定律的领域,根据大数定律,数学上可以表示:
lim N → ∞ 1 N ∑ i = 1 N W i = E ( W ) \lim_{N\to\infty}\dfrac{1}{N}\sum_{i=1}^NW_i=\text{E}(W) limN→∞N1∑i=1NWi=E(W)
Var ( 1 N ∑ W i ) ∝ 1 N → 0 \text{Var}(\dfrac{1}{N}\sum W_i)\propto\dfrac{1}{N}\to 0 Var(N1∑Wi)∝N1→0
这意味着,在网络核心,网络看到的不再是一条条锯齿,而是一片稳定流动的束。越靠近网络中心,统计特征越明显,个体特征越不明确。
self-clock 下的 AIMD 自动适应了这个统计意义上的大数定律的世界,因为 self-clock 仅做时钟,如果将它做信息本身,比如尝试从其到达的波动规律中获益,那就破坏了作为时钟的公平,因为 “ACK 的到达” 本身就携带了全局的,公平的指示,简直就是在告诉你,反应,而非利用。
比如 BBR 尝试基于 self-clock 做 delivery rate 测量,就是不公平的,就好像如果分子将某个密度差作为某种趋势的预测,就不会形成温度(温度就是公平前提下的概念)的概念,统计力学也不会存在。
TCP/IP 的尽力而为模型就是提供了一个真空盒子,释放进去的气体经过一定时间一定是均匀的,不要试图利用统计波动破坏统计特征,网络就是一个多节点组合成的整体,全局意义的统计特征就是它的一个本质属性。
即使你证明了你能通过利用波动特征而让整体短暂获益,收益也很快会被不可扩展性反噬。至于不可扩展性,如之前的文章所述,在线性流逝的时间中,算法能跟得上非线性系统的指数级信息释放速度吗,换句话说,你只能像个自私的经理,让个体获益,根本无力让整体向好。
浙江温州皮鞋湿,下雨进水不会胖。
1万+

被折叠的 条评论
为什么被折叠?



