BBR 的 buffer 动力学观感

这周很忙,今天还加了一天班,但还是抽空实现了五一在安徽泾县山区喝着一壶酒写的 BBR ProbeRTT 的想法,没多少行代码,它真就消除了带宽锯齿,皮了个鞋👞,昨天我还在群里说了今天再说说 BBR 的,回到家就作图作文了。

去年夏天,我花了些精力量化 BBR 动力学,并给出一些相图以及收敛方程的数值解,但数学也只是描述而非解释,就像牛顿定律只能描述万有引力却无法解释引力的由来(任何理论都无法解释)一样。所以这个话题要不断推敲,常看常新,搞不好就有新思路了。

BBR 动力学核心是收敛,归为一句话就是 “带宽越大 Probe 加速比越小”,这句话是收敛的原因,进一步通俗解释,大的想更大更难,是为边际收益递减,小的想更大也难,但边际收益单调递减,却仍高于大的,相对而言其加速比就更大。buffer 动力学解释,即小流 Probe 的结果比大流的更大,大流就表现出净损而让给小流了。

《道德经》里早有论述,“天之道,损有余而补不足”,看来这是天道,容不得解释。而 “人之道则不然,损不足以奉有余”,则与《新约 · 马太福音》相呼应了。

我也常常提到 “矩”,它是一个乘积,与一个值不同的是,它体现了两个维度的胶合,两个维度的矩针对一个维度的值,其结果就是边际收益递减了,因为两个维度的伸缩比例并不常一致,所谓矩的效应,就比如面积相同而周长不等,或周长相同而面积各异,正如力和力臂,矩不变,但费料不同。

套用解释 buffer 动力学,BDP 就是挤出带宽的矩,BBR 靠 1.25 倍增益来体现边际收益递减,直到当它继续再减下去时,对方也要跟着减下去,这时候收敛到公平,方可停止,惊奇的是,这件事确实自然发生。

5 月 2 日晚,我在安徽宣城写了一篇 BBR ProbeRTT 新改,提出一个新思路,近日测试,是那么回事。我大致说的是,BBR 一定需要借宿于 buffer 中收敛到公平,为守其初衷,却又要 ProbeRTT,但原生 ProbeRTT 的假设太苛刻,对全局同步过度依赖,这境况在强弱不等的持续统计波动中未必能保持,何不哺其糟而歠其醨,将自己因 Probe 而建立的 queue 吐出即可。

我模仿 Jaffe’s style,用四则混合运算描述 BBR buffer 动力学,基于表达式推出一个结论,基于该结论给出上述 ProbeRTT 描述,即刻取消 BBR 由于 ProbeRTT 导致的邪恶带宽锯齿(BBR 终于摆脱了 cwnd 锯齿,却为何还要面对 bw 锯齿)。

设总带宽为 1,流 1 带宽为 x,则流 2 带宽则为 1 - x,以 ProbeBW 之 ProbeUP phase 论,流 1 和流 1 的带宽增量表示为:

f 1 ( x ) = g ⋅ x g ⋅ x + 1 − x − x x + 1 − x f_1(x)=\dfrac{\mathrm{g}\cdot x}{\mathrm{g}\cdot x+1-x}-\dfrac{x}{x+1-x} f1(x)=gx+1xgxx+1xx

f 2 ( x ) = g ⋅ ( 1 − x ) g ⋅ ( 1 − x ) + x − ( 1 − x ) ( 1 − x ) + x f_2(x)=\dfrac{\mathrm{g}\cdot (1-x)}{\mathrm{g}\cdot (1-x)+x}-\dfrac{(1-x)}{(1-x)+x} f2(x)=g(1x)+xg(1x)(1x)+x(1x)

不必化简,直接画图:
在这里插入图片描述

最下面灰线是 f1(x) - f2(x),可清晰看出 x = 0.5 的分水岭,当 x < 0.5,f1(x) 属小流带宽演进,f2(x) 属大流带宽演进,小流挤压大流带宽,反之亦然。

这也不难直观理解,以 AIMD 为例,当 x,y 分别为两流不同的初始 cwnd 时,设 x < y,那么 x + L y + L \dfrac{x+L}{y+L} y+Lx+L 当 L 越大,值越趋向 1 收敛到公平,即只要 additive increase 一直持续,总会收敛,BBR 同样,每次 Probe 都会堆一小丢丢 queue,大流堆的少,小流堆得更多些, x + L b i g g e r y + L s m a l l e r \dfrac{x+L_{bigger}}{y+L_{smaller}} y+Lsmallerx+Lbigger 看起来要比 AIMD 更快趋向 1,但 L 都超级小(如图所示,我还特意缩放了坐标比例),总体上还是 AIMD 应有的收敛速度。

f1(x),f2(x) 显然关于 x = 0.5 对称,它们之做差结果显示了 “损有余而补不足” 的过程,该过程强度逐渐收缓(如图绿蓝线之间的间隙),直至两者再无差异,它们与 x = 0.5 相交的此点即稳定点,它是不动点。

依照上述 5 月 2 日的 ProbeRTT 方法,释放自己堆积的 queue,在上图的意义就是分别求绿线 x 到 0.5 以及蓝线 0.5 到 1 - x 的积分,肉眼观测,面积是相等的,大差不差一个相位。

现在演示一下实际上是不是这样:

import numpy as np
import matplotlib.pyplot as plt
import randomx0 = 0.2  
y0 = 0.8 
g = 1.25  
bx, by = 0, 0
R = 2
C = 1num_steps = 500
t = np.arange(num_steps)  # 带宽
x = np.zeros(num_steps)
y = np.zeros(num_steps)
# BDP
Bx = np.zeros(num_steps)
By = np.zeros(num_steps)
# RTwait
r = np.zeros(num_steps)
# 自己堆占的 queue
Ax = np.zeros(num_steps)
Ay = np.zeros(num_steps)
# 自己堆栈 queue 的平均
sAx = np.zeros(num_steps)
sAy = np.zeros(num_steps)x[0] = x0
y[0] = y0
r[0] = 0Bx[0], By[0] = x0, y0
Ax[0], Ay[0], sAx[0], sAy[0] = 0, 0, 0, 0
nx, ny = random.randint(5, 15), random.randint(5, 15)for i in range(1, num_steps):if i > 150:C = 5if i > 350:C = 0.2dec = 0x[i] = C * (g * x[i-1]) / (g * x[i-1] + y[i-1]) tx = C * x[i-1] / (x[i-1] + y[i-1])bx += R * (x[i] - tx)if i % nx == 0: # x 随意 ProbeRTT,不再依赖同步dec += bxbx = 0      # 退掉自己 Probe 造成的 queue,堆多少退多少nx = random.randint(5, 15)Ax[i] = bxsAx[i] = 0.9 * sAx[i-1] + 0.1 * bx#y[i-1] = C - x[i]   # 忘记 max-filter bwy[i] = C * (g * y[i-1]) / (g * y[i-1] + x[i])  ty = C * y[i-1]/(y[i-1] + x[i])by += R * (y[i] - ty)if i % ny == 0: # y 随意 ProbeRTT,不再依赖同步dec += byby = 0      # 退掉自己 Probe 造成的 queue,堆多少退多少ny = random.randint(5, 15)Ay[i] = bysAy[i] = 0.9 * sAy[i-1] + 0.1 * by#x[i] = C - y[i]  # 忘记 max-filter bwr[i] = (((x[i] + y[i]) * R) + bx + by- dec) / C - Rif r[i] < 0:r[i] = 0Bx[i] = r[i] * x[i]By[i] = r[i] * y[i]fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6))ax1.plot(t, r, label='rtt(t)', color='green')ax1.plot(t, x, label='bw_x(t)', linestyle=':', color='blue')
ax1.plot(t, y, label='bw_y(t)', linestyle=':', color='red')
ax2.plot(t, Bx, label='bdp_x(t)', linestyle=':')
ax2.plot(t, By, label='bdp_y(t)', linestyle=':')
ax3.plot(t, Ax, label='buf_x(t)', linestyle=':')
ax3.plot(t, Ay, label='buf_y(t)', linestyle=':')
ax3.plot(t, sAx, label='avg_buf_x(t)', linestyle='-')
ax3.plot(t, sAy, label='avg_buf_y(t)', linestyle='-')
ax1.set_xlabel('t')
ax1.set_ylabel('v')
ax2.set_xlabel('t')
ax2.set_ylabel('BDP')
ax2.set_xlabel('t')
ax2.set_ylabel('total buffer')
ax1.legend()
ax1.grid(True)
ax2.legend()
ax2.grid(True)
ax3.legend()
ax3.grid(True)
plt.show()

给出几个结局:
在这里插入图片描述

观测到的现象是:

  • total buffer 写错了,应该去掉,因为已经有了图例;
  • RTT 总体在一个很小范围内波动,视 ProbeRTT 平均期望窗口而定,窗口越大,RTT 振幅越大;
  • bw,BDP,自己堆积 queue 总体都属于统计公平状态;
  • BDP 随总带宽 C 而涨跌,但整体非常公平;

这才像个真实的样子,没有任何假设和约束,但却收敛到了公平。

但这里有个 BBR 细节,“为什么要在一个 max-filter bw 窗口内至少进入 Probe phase 一次”,比如 10-round 的 max-filter bw 窗口,要设计 8-round 的 ProbeBW cycle。如果将上述 python 代码的两行 “忘记 max-filter bw” 注释放开,结局如下:
在这里插入图片描述

这个结果写出递推式就能看明白,完全不必分析不动点和相图。

最后,给出完全由 buffer 动力学驱动而不是照着 “算法” 临摹的实际效果。还是先给出仿真代码:

for i in range(1, num_steps):if i > 150:C = 5if i > 350:C = 0.2dec = 0# 根据 buffer 占比求带宽x[i] = C * (g * x[i-1] * R) / (g * x[i-1] * R + y[i-1] * (r[i-1] + R)) # 如果不 probe 的带宽tx = C * x[i-1] * (r[i-1] + R) / (x[i-1] * (r[i-1] + R) + y[i-1] * (r[i-1] + R))bx += R * (x[i] - tx)# 随机而生的 ProbeRTT,不依赖同步if i % nx == 0:dec += bxbx = 0nx = random.randint(5, 15)# 即时 bufferAx[i] = bx# 即时 buffer 移动指数平均sAx[i] = 0.9 * sAx[i-1] + 0.1 * bx# 同 x,略y[i] = C * (g * y[i-1] * R) / (g * y[i-1] * R + x[i] * (r[i-1] + R))  ty = C * y[i-1] * (r[i-1] + R)/(y[i-1] * (r[i-1] + R) + x[i] * (r[i-1] + R))by += R * (y[i] - ty)if i % ny == 0:dec += byby = 0ny = random.randint(5, 15)Ay[i] = bysAy[i] = 0.9 * sAy[i-1] + 0.1 * by# RTwait = total_bdp / C - RTpropr[i] = (((x[i] + y[i]) * R) + bx + by- dec) / C - Rif r[i] < 0:r[i] = 0Bx[i] = r[i] * x[i]By[i] = r[i] * y[i]

以下几个例子的实际采样结局:
在这里插入图片描述

真正的统计律主导,不以精确而以统计应对时局,可以看出 queue 以一个非常有限的振幅波动,没有完全消除,但也不过度增加,且 ProbeRTT 也不再需同步,任意时刻执行均可(在一个最大窗口比如 5~10s 的约束内),它完全通过 buffer 动力学驱动。整体而言,这达到了一个松散的统计公平。

浙江温州皮鞋湿,下雨进水不会胖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第9讲、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架构的核心组件&#xff0c;也是现代深度学习中最重要的注意力机制之一。本文将从原理、实现和应用三个方面深入剖析这一机制。 1. 基本原理 Scaled Dot-Product Attention的本质是一种加权求和机制&#xff0c;通过计算查询(Query…

el-tree结合checkbox实现数据回显

组件代码 <el-tree:data"vertiList"show-checkboxnode-key"id":props"defaultProps"ref"treeRefx"class"custom-tree"check-change"handleCheckChange"> </el-tree>获取选择的节点 handleCheckChan…

OpenResty 深度解析:构建高性能 Web 服务的终极方案

引言 openresty是什么&#xff1f;在我个人对它的理解来看相当于嵌入了lua的nginx; 我们在nginx中嵌入lua是为了不需要再重新编译,我们只需要重新修改lua脚本,随后重启即可; 一.lua指令序列 我们分别从初始化阶段&#xff0c;重写/访问阶段&#xff0c;内容阶段&#xff0c;日志…

多商户商城系统源码解析:开发直播电商APP的技术底层实战详解

随着直播电商的火爆&#xff0c;越来越多的创业者和企业都在寻求打造自己的多商户商城系统&#xff0c;以实现“人、货、场”三者的深度融合。然而&#xff0c;从一个简单的电商平台到一个功能完善的直播电商APP&#xff0c;其技术底层架构和实现过程并非一蹴而就。本文将从架构…

桌面端进程通信

以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…

openEuler24.03 LTS下安装MySQL8.0.42

目录 前提步骤 删除原有mysql及maridb数据库 安装MySQL 启动MySQL 启动查看MySQL状态 设置MySQL开机自启动 查看登录密码 登录MySQL 修改密码及支持远程连接 远程连接MySQL 前提步骤 拥有openEuler24.03 LTS环境&#xff0c;可参考&#xff1a;Vmware下安装openEule…

idea 保证旧版本配置的同时,如何从低版本升到高版本

文章目录 前言idea 保证旧版本配置的同时,如何从低版本升到高版本1. 备份项目2. 下载最新的idea3. 安装安装包4. 导入idea2019旧配置5. 验证前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差,…

填坑记: 古董项目Apache POI 依赖异常排除

当你看到NoSuchMethodError的时候&#xff0c;不要慌&#xff0c;深呼吸&#xff0c;这可能只是JAR包版本的问题… 引子&#xff1a;一个平静的周二下午 那是一个看似平常的周二下午&#xff0c;系统运行良好&#xff0c;开发团队在有条不紊地推进着新功能的开发。突然&#x…

CAPL Class: TcpSocket (此类用于实现 TCP 网络通信 )

目录 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函数调用的基本流程服务器端的基本流程客户端的基本流程Class: TcpSocket学习笔记。来自CANoe帮助文档。 Class: TcpSocket accept /…

微信小程序的开发及问题解决

HttpClient 测试例子 SpringBootTest public class HttpClientTest {/*** 测试通过httpclient发送get方式的请求*/Testpublic void testGET() throws IOException {//创建httpclient对象CloseableHttpClient httpClient HttpClients.createDefault();//创建请求对象HttpGet ht…

foreach中使用await的问题

目录 1.说明 2.示例 3.解决方案 1.说明 在foreach中调用异步方法&#xff0c;即使使用了await&#xff0c;不会依次执行每个异步任务&#xff0c;也就是说Array.prototype.forEach不会等待 Promise 完成&#xff0c;即使你在回调函数中返回一个 Promise&#xff0c;forEach …

Linux调试生成核心存储文件

1.核心存储文件配置&#xff1a; 不知道理解对不对&#xff0c;Linux中的核心存储文件的配置是在/proc/sys/kernel/core_pattern中的&#xff0c;使用 cat /proc/sys/kernel/core_pattern # 打印出 |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E表示核…

Compose笔记(二十三)--多点触控

这一节主要了解一下Compose中多点触控&#xff0c;在Jetpack Compose 中&#xff0c;多点触控处理需要结合Modifier和手势API来实现&#xff0c;一般通过组合 pointerInput、TransformableState 和 TransformModifier 来创建支持缩放、旋转和平移的组件。 一、 API 1. Pointer…

【Java ee初阶】HTTP(4)

构造HTTP请求 1&#xff09;开发中&#xff0c;前后端交互。浏览器运行的网页中&#xff0c;构造出HTTP请求 2&#xff09;调试阶段&#xff0c;通过构造HTTP请求测试服务器 朴素的方案&#xff1a; 通过tcp socket 的方式构造HTTP请求 按照HTTP请求格式&#xff0c;往TCP…

STM32 __main

STM32开发中__main与用户main()函数的本质区别及工作机制 在STM32开发中&#xff0c;__main和用户定义的main()函数是启动过程中的两个关键节点&#xff0c;分别承担运行时初始化和用户程序入口的职责。以下是它们的核心差异及协作机制&#xff1a; 一、定义与层级差异 ​__ma…

什么是PMBus

一、PMBus的定义与背景 PMBus&#xff08;Power Management Bus&#xff0c;电源管理总线&#xff09; 是一种基于SMBus&#xff08;System Management Bus&#xff09;的开放标准数字通信协议&#xff0c;专为电源设备的监控、配置和控制设计。由PMBus联盟&#xff08;现并入…

Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法

Python方法类型全解析&#xff1a;实例方法、类方法与静态方法的使用场景 一、三种方法的基本区别二、访问能力对比表三、何时使用实例方法使用实例方法的核心场景&#xff1a;具体应用场景&#xff1a;1. 操作实例属性2. 对象间交互3. 实现特定实例的行为 四、何时使用类方法使…

业务中台-典型技术栈选型(微服务、容器编排、分布式数据库、消息队列、服务监控、低代码等)

在企业数字化中台建设中&#xff0c;业务中台是核心支撑平台&#xff0c;旨在通过技术手段将企业核心业务能力抽象、标准化和复用&#xff0c;以快速响应前端业务需求。其核心技术流涉及从业务抽象到服务化、治理和持续优化的全流程。以下是业务中台建设中的核心技术体系及关键…

期望是什么:(无数次的均值,结合概率)21/6=3.5

https://seeing-theory.brown.edu/basic-probability/cn.html 期望是什么:(无数次的均值,结合概率)21/6=3.5 一、期望(数学概念) 在概率论和统计学中,**期望(Expectation)**是一个核心概念,用于描述随机变量的长期平均取值,反映随机变量取值的集中趋势。 (一…

matlab官方免费下载安装超详细教程2025最新matlab安装教程(MATLAB R2024b)

文章目录 准备工作MATLAB R2024b 安装包获取详细安装步骤1. 文件准备2. 启动安装程序3. 配置安装选项4. 选择许可证文件5. 设置安装位置6. 选择组件7. 开始安装8. 完成辅助设置 常见问题解决启动失败问题 结语 准备工作 本教程将帮助你快速掌握MATLAB R2024b的安装技巧&#x…