论 Linux 内核态全局稳态带宽的卡尔曼估计与工程实现

论 Linux 内核态全局稳态带宽的卡尔曼估计与工程实现

KCC 在内核中维护了一个全局卡尔曼滤波器,从所有连接 PROBE_BW 巡航阶段(增益=1.0)采集交付带宽,估计整机瓶颈带宽。新连接建立时,从该估计值中切取一个保守比例注入初始 pacing rate 和 CWND 跟踪器,以绕开传统冷启动的盲目探测。本文讨论该全局滤波器的建模、观测门控、定点数迭代、注入策略以及内核插入逻辑。

1. 全局滤波器工作概览

#mermaid-svg-afXlDCnOO7ZZMUrB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-afXlDCnOO7ZZMUrB .error-icon{fill:#552222;}#mermaid-svg-afXlDCnOO7ZZMUrB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-afXlDCnOO7ZZMUrB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-afXlDCnOO7ZZMUrB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-afXlDCnOO7ZZMUrB .marker.cross{stroke:#333333;}#mermaid-svg-afXlDCnOO7ZZMUrB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-afXlDCnOO7ZZMUrB p{margin:0;}#mermaid-svg-afXlDCnOO7ZZMUrB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster-label text{fill:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster-label span{color:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster-label span p{background-color:transparent;}#mermaid-svg-afXlDCnOO7ZZMUrB .label text,#mermaid-svg-afXlDCnOO7ZZMUrB span{fill:#333;color:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB .node rect,#mermaid-svg-afXlDCnOO7ZZMUrB .node circle,#mermaid-svg-afXlDCnOO7ZZMUrB .node ellipse,#mermaid-svg-afXlDCnOO7ZZMUrB .node polygon,#mermaid-svg-afXlDCnOO7ZZMUrB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-afXlDCnOO7ZZMUrB .rough-node .label text,#mermaid-svg-afXlDCnOO7ZZMUrB .node .label text,#mermaid-svg-afXlDCnOO7ZZMUrB .image-shape .label,#mermaid-svg-afXlDCnOO7ZZMUrB .icon-shape .label{text-anchor:middle;}#mermaid-svg-afXlDCnOO7ZZMUrB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-afXlDCnOO7ZZMUrB .rough-node .label,#mermaid-svg-afXlDCnOO7ZZMUrB .node .label,#mermaid-svg-afXlDCnOO7ZZMUrB .image-shape .label,#mermaid-svg-afXlDCnOO7ZZMUrB .icon-shape .label{text-align:center;}#mermaid-svg-afXlDCnOO7ZZMUrB .node.clickable{cursor:pointer;}#mermaid-svg-afXlDCnOO7ZZMUrB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-afXlDCnOO7ZZMUrB .arrowheadPath{fill:#333333;}#mermaid-svg-afXlDCnOO7ZZMUrB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-afXlDCnOO7ZZMUrB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-afXlDCnOO7ZZMUrB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-afXlDCnOO7ZZMUrB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-afXlDCnOO7ZZMUrB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-afXlDCnOO7ZZMUrB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster text{fill:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB .cluster span{color:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-afXlDCnOO7ZZMUrB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-afXlDCnOO7ZZMUrB rect.text{fill:none;stroke-width:0;}#mermaid-svg-afXlDCnOO7ZZMUrB .icon-shape,#mermaid-svg-afXlDCnOO7ZZMUrB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-afXlDCnOO7ZZMUrB .icon-shape p,#mermaid-svg-afXlDCnOO7ZZMUrB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-afXlDCnOO7ZZMUrB .icon-shape .label rect,#mermaid-svg-afXlDCnOO7ZZMUrB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-afXlDCnOO7ZZMUrB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-afXlDCnOO7ZZMUrB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-afXlDCnOO7ZZMUrB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

拒绝
通过
KCC 连接 PROBE_BW

增益=1.0 巡航阶段
采集交付带宽

bw = delivered / interval_us
滤波器激活?
首次样本

无门控强制信任
卡方门控

野值过滤
返回原估计值 x
卡尔曼更新

定点数防溢出迭代
更新 x 和 P
atomic64 写回全局状态
新连接创建时

读取 kf_x 计算甜品速度

2. 状态模型

全局瓶颈带宽被建模为一个一维、准静态的随机游走:

复制代码
x_k = x_{k-1} + w_k,   w_k ~ N(0, Q)
z_k = x_k + v_k,       v_k ~ N(0, R_k)
  • x --- 全局瓶颈带宽(内部单位 BW_UNIT)。

  • z --- 单条流在增益 1.0 巡航阶段测得的交付带宽。

  • Q --- 过程噪声方差。其作用并非描述带宽的真实物理波动,而是防止滤波器在长期运行后因协方差 P 衰减至零而丧失对新观测的响应能力(滤波器麻木)。实现上每次迭代固定叠加 Q = 1 << q_shift,且强制 P ≥ Q

  • R_k --- 观测噪声方差,采用与带宽量级成比例的相对模型:

    R_k = (z_k * r_pct / 100)^2

冷启动阶段 r_pct 使用 startup_r_pct_val(默认 20%),稳态后切换为 steady_r_pct_val(默认 5%)。较大的启动 R 压低初始样本的卡尔曼增益,避免单一观测将状态锁死;5% 的稳态值是在 1 Gbps 链路上经测试确定的,能在正常抖动和响应速度之间取得可用的平衡。

3. 卡方门控

公网路径上,交付带宽会出现瞬时归零或暴增的非高斯野值。这些样本若不加过滤直接送入更新方程,会将全局状态拉偏。为此,在执行状态更新之前,使用基于马氏距离的卡方检验进行门控:

复制代码
y = z - x
S = P + R
若 y² / S > χ²_{1,α} ,则丢弃该样本

在自由度 1 下,阈值 3.84 对应 95% 置信度,6.63 对应 99%。内核代码为避免 的 64 位溢出和浮点运算,将分子分母同步右移后做整数比较。

4. 64 位定点数更新与防溢出

标准卡尔曼更新公式:

复制代码
x = (x·R + z·P) / (P + R)
P = (P·R) / (P + R)

在 10 Gbps 量级下,PR 的数值可能极大,分子中 x·RP·R 易超出 u64 范围。内核不允许使用浮点,也无法随意引入 128 位除法。实现上采用动态等比右移:

  1. PRP+R 中的最大值 max_v
  2. 只要 max_v >= 2^31,就将 PR 同步右移一位,记录移位数 shift
  3. 使用缩放后的值计算:

由于分子分母等比缩放,状态 x 的估计无精度损失。协方差 P 缩小了 2^shift 倍,左移恢复后仅在低位有截断误差,对滤波器行为不构成实质影响。整个更新仅使用标准 u64 乘除运算。

5. 采样时机的插入逻辑

样本投喂的插入点位于 BBR_mainUCP_main(内核 CC 回调入口函数),仅当 round_start == true 且处于 PROBE_BW 巡航增益 1.0 时触发:

cpp 复制代码
/* 
 * 插入位置:BBR_main 或 UCP_main 函数内,
 * 在每轮结束 (round_start == true) 时执行。
 * 严格限制在 PROBE_BW 模式且 pacing_gain == BBR_UNIT (1.0x)
 * 此时链路不积队列亦不主动排空,交付带宽最接近物理瓶颈。
 */
if (ucp_kf_enable && ucp->round_start &&
    ucp->mode == UCP_PROBE_BW && ucp->pacing_gain == BBR_UNIT) {
    
    // 1. 根据本轮交付数据量与时间间隔计算瞬时交付带宽
    u64 bw = (u64)rs->delivered * BW_UNIT / max_t(u32, rs->interval_us, 1U);
    
    // 2. 路由到全局卡尔曼更新函数
    if (!atomic_read(&ucp_kf_active)) {
        // 滤波器尚未激活:冷启动,使用较大的 R (startup_r_pct),
        // 且关闭门控 (check = false),完全信任第一个样本
        ucp_kf_update(bw, (u32)ucp_kf_startup_r_pct_val, false);
    } else {
        // 滤波器已激活:使用稳态 R (steady_r_pct),
        // 开启卡方门控 (check = true),过滤非高斯野值
        ucp_kf_update(bw, (u32)ucp_kf_steady_r_pct_val, true);
    }
}

排除的采样时机:

  • STARTUP(增益 ≈2.89):交付带宽中包含因增益放大而产生的人为排队成分。
  • DRAIN(增益 ≈0.35):主动压低发送速率以排空队列,测量值偏低。
  • PROBE_BW 的 1.25×/0.75× 增益相位:为探测上下界而有意偏离稳态。

只有增益恰好为 1.0 的巡航槽内,pacing rate 等于当前的瓶颈带宽估计,此时的交付带宽是对物理瓶颈最干净的观测。样本稀疏,但污染度极低。

6. 卡尔曼滤波更新算法实现(带中文注解)

cpp 复制代码
/* ---- 全局卡尔曼滤波 BDP 更新函数 --------------------------------- */
/* 根据观测值 z(交付带宽)和噪声百分比 r_pct 更新全局状态 x 和协方差 P。
 * check 参数控制是否启用卡方门控。                                */
static u64 ucp_kf_update(u64 z, u32 r_pct, bool check)
{
    // 读取当前的协方差 P 和状态估计 x
    u64 P = atomic64_read(&ucp_kf_P);
    u64 x = atomic64_read(&ucp_kf_x);
    // 计算测量噪声 R = (z * r_pct%)^2,采用相对百分比模型
    u64 R = ucp_kf_compute_R(z, r_pct);
    u32 shift = 0;
    u64 Pcopy, Rcopy, denom;
    s64 delta;

    // 过程噪声注入:固定加上 Q = 1 << ucp_kf_q_shift
    // 防止长期运行后 P 归零导致滤波器麻木
    P += (1ULL << ucp_kf_q_shift);

    // 冷启动处理:若滤波器未激活,直接用当前观测初始化
    if (unlikely(!atomic_read(&ucp_kf_active))) {
        atomic64_set(&ucp_kf_x, z);               // 初始状态设为观测值
        atomic64_set(&ucp_kf_P, max(R, 1ULL));    // 初始协方差取 R 和 1 的较大值
        atomic_set(&ucp_kf_active, 1);            // 标记滤波器已激活
        return z;
    }

    // 卡方门控:过滤非高斯野值
    if (check) {
        delta = (s64)z - (s64)x;                 // 残差 y = z - x
        u64 nu2 = (u64)(delta < 0 ? -delta : delta); // |delta|
        u64 S = P + R;                           // 残差协方差 S = P + R
        if (S > 0) {
            // 避免溢出和浮点:右移近似
            nu2 = (nu2 >> 10) * (nu2 >> 10);    // 近似 y^2 / 2^20
            S >>= 20;                            // S / 2^20
            // 比较 (y^2 / S) 与卡方阈值 (chi2_num / chi2_den)
            if (S > 0 && nu2 / S >
                (u64)ucp_kf_chi2_num_val / (u64)ucp_kf_chi2_den_val)
                return x;   // 门控未通过,丢弃样本,维持原估计
        }
    }

    // 动态等比右移,防止乘法溢出
    Pcopy = P;
    Rcopy = R;
    {
        u64 max_v = Pcopy;
        if (Rcopy > max_v) max_v = Rcopy;
        if (Pcopy + Rcopy > max_v) max_v = Pcopy + Rcopy;
        // 只要最大值 >= 2^31,就同步右移,确保相乘不超 64 位
        while (max_v >= (1ULL << 31)) {
            Pcopy >>= 1; Rcopy >>= 1; max_v >>= 1; shift++;
        }
    }

    denom = Pcopy + Rcopy;
    if (denom == 0)
        return x;

    // 卡尔曼状态更新:x = (x*R + z*P) / (P + R)
    x = (x * Rcopy + z * Pcopy) / denom;

    // 协方差更新:P = (P*R) / (P+R),注意结果缩小了 2^shift 倍
    P = Pcopy * Rcopy / denom;

    // 恢复协方差的原始量级
    if (shift > 0) P <<= shift;

    // 协方差下限保护,防止 P 低于过程噪声,保持滤波器响应能力
    {
        u64 q = 1ULL << ucp_kf_q_shift;
        if (P < q) P = q;
    }

    // 写回全局原子变量(无锁)
    if (x > 0) {
        atomic64_set(&ucp_kf_x, x);
        atomic64_set(&ucp_kf_P, P);
    }
    return x;
}

/* 辅助函数:计算测量噪声 R = (z * pct%)^2 */
static u64 ucp_kf_compute_R(u64 z, u32 pct)
{
    u64 r = z * (u64)pct / 100;
    return r * r;
}

7. 新连接初始注入

全局滤波器收敛后,新建连接通过以下函数获取初始带宽,流程如下:
连接自身状态 (cwnd, srtt) 全局原子变量 (kf_x, kf_active) 新连接 kcc_init() 连接自身状态 (cwnd, srtt) 全局原子变量 (kf_x, kf_active) 新连接 kcc_init() #mermaid-svg-As9XKHuCR9HLCkYF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-As9XKHuCR9HLCkYF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-As9XKHuCR9HLCkYF .error-icon{fill:#552222;}#mermaid-svg-As9XKHuCR9HLCkYF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-As9XKHuCR9HLCkYF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-As9XKHuCR9HLCkYF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-As9XKHuCR9HLCkYF .marker.cross{stroke:#333333;}#mermaid-svg-As9XKHuCR9HLCkYF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-As9XKHuCR9HLCkYF p{margin:0;}#mermaid-svg-As9XKHuCR9HLCkYF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-As9XKHuCR9HLCkYF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-As9XKHuCR9HLCkYF .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-As9XKHuCR9HLCkYF .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-As9XKHuCR9HLCkYF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-As9XKHuCR9HLCkYF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-As9XKHuCR9HLCkYF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-As9XKHuCR9HLCkYF .sequenceNumber{fill:white;}#mermaid-svg-As9XKHuCR9HLCkYF #sequencenumber{fill:#333;}#mermaid-svg-As9XKHuCR9HLCkYF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-As9XKHuCR9HLCkYF .messageText{fill:#333;stroke:none;}#mermaid-svg-As9XKHuCR9HLCkYF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-As9XKHuCR9HLCkYF .labelText,#mermaid-svg-As9XKHuCR9HLCkYF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-As9XKHuCR9HLCkYF .loopText,#mermaid-svg-As9XKHuCR9HLCkYF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-As9XKHuCR9HLCkYF .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-As9XKHuCR9HLCkYF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-As9XKHuCR9HLCkYF .noteText,#mermaid-svg-As9XKHuCR9HLCkYF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-As9XKHuCR9HLCkYF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-As9XKHuCR9HLCkYF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-As9XKHuCR9HLCkYF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-As9XKHuCR9HLCkYF .actorPopupMenu{position:absolute;}#mermaid-svg-As9XKHuCR9HLCkYF .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-As9XKHuCR9HLCkYF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-As9XKHuCR9HLCkYF .actor-man circle,#mermaid-svg-As9XKHuCR9HLCkYF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-As9XKHuCR9HLCkYF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt init_bw \< 下限 通过 alt 未激活 已激活 读取 kf_active 返回 0 (不注入) 读取 kf_x (公平份额估计) fair = kf_x init_bw = fair * discount_num / discount_den init_bw = init_bw * BBR_UNIT / high_gain 读取 cwnd, srtt 计算下限 返回 0 (注入无意义) 返回 init_bw 设置 sk_pacing_rate, snd_cwnd, minmax 历史

注入公式:

c 复制代码
init_bw = (fair * discount_num / discount_den) / high_gain;
  • fair 读取自 atomic64_read(&kf_x)
  • discount_num/discount_den 默认为 50/100,即取估计值的一半。
  • high_gain 为 STARTUP 增益,2885/1000 ≈ 2.885。

代入后有效注入系数约为瓶颈带宽的 0.5 / 2.885 ≈ 17.3%。乘以 STARTUP 增益后,实际首轮 pacing rate 约为瓶颈的 50%,避免因增益放大而直接过冲。discount_num 的可调范围为 35--75,对应有效系数 12.1%--25.9%。

初始注入值不追求准确。全局估计仅为统计平均,实际可用带宽可能偏高或偏低。KCC 单流卡尔曼与状态机能够在 2--4 个 RTT 内判断带宽的调整方向并完成修正,因此初始值只需落在可快速纠偏的区间内。

此外,注入的 pacing_rate 是发送速率的上限,实际发送量受拥塞窗口(CWND)约束。新连接的 CWND 初始固定为 TCP_INIT_CWND(10 个段,约 15 KB),在 200 ms RTT 路径上对应的最大速率约为 600 Kbps。CWND 随 ACK 逐步扩大,KCC 在每个 ACK 依据带宽和 RTT 估计重新计算目标 cwnd,执行 cwnd = min(cwnd + acked, target) 的收敛过程,而非无约束发包。因此,前若干 RTT 内的实际发送速率远低于 pacing_rate 所设定的上限,初始注入值主要作用于 CWND 打开后的收敛起点。

8. 新连接注入函数实现(带中文注解)

cpp 复制代码
/* 获取新连接的初始带宽估计(甜品速度)*/
static u64 ucp_kf_get_init_bw(struct sock* sk)
{
    struct tcp_sock* tp = tcp_sk(sk);
    u64 fair, init_bw;

    // 检查全局 KF 是否启用且已激活
    if (!ucp_kf_enable || !atomic_read(&ucp_kf_active))
        return 0;

    // 读取全局瓶颈带宽估计
    fair = (u64)atomic64_read(&ucp_kf_x);
    if (fair == 0)
        return 0;

    // 按折扣比例打折,得到公平份额的初始带宽
    init_bw = fair * (u64)ucp_kf_discount_num_val / (u64)ucp_kf_discount_den_val;

    // 逆向补偿 STARTUP 增益:避免乘上 2.885 后冲顶
    init_bw = init_bw * BBR_UNIT / (u64)ucp_high_gain_val;

    // 保底下限:注入值不得低于当前 cwnd 所能达到的基础速率
    if (init_bw < (u64)tcp_snd_cwnd(tp) * (u64)BBR_UNIT /
        max_t(u32, tp->srtt_us >> 3, 1U))
        return 0;

    return init_bw;
}

9. 无锁全局状态

全局变量 kf_xkf_Pkf_active 均通过 atomic64 进行读写,未使用锁。

  • 写入发生在软中断上下文,每条流每 RTT 至多触发一次。
  • 读出仅在新连接创建时发生。
  • 读操作可能观察到 x 已更新而 P 尚未更新的中间状态,这可能导致某次注入值出现微小抖动,但在下一次连接创建时状态已必然一致,不会造成持久性偏差。

与引入自旋锁或 RCU 的开销相比,无锁方案对数据路径的影响可以忽略。

10. 路径分组的取舍

全局滤波器基于单一瓶颈假设。若服务器同时承载途经不同 AS 出口的流,不同路径的瓶颈带宽并不相同,样本混合会使估计值偏离各自真实的瓶颈。KCC 未在内核中实现按目的网段或路由标记的路径分组机制。这一取舍并非出于实现复杂度的不可行,而是因为维护路径到状态的映射表在软中断上下文中开销过大,且分组后各组内样本更稀疏,滤波器收敛更慢,反而可能得不偿失。对于因混合路径产生的估计偏差,KCC 依靠单流在 2--4 个 RTT 内的快速修正机制吸收。

在多流竞争场景中,首秒重传数略高是普遍现象,BBR、CUBIC、PCC 等算法均无法避免,KCC 同样不能根本性解决。但 KCC 的注入策略并未显著恶化这一状况。尽管 KCC 拥有一个预测的甜品启动速度,实际行为并不横冲直撞:发送速率受 CWND 初始值严格约束,CWND 在启动过程中随观测逐步打开,这与其它拥塞控制算法一致。在成都至香港、成都至美国、香港至美国等真实路径的 iperf3 测试中,KCC 在多流竞争下的首秒重传表现与同类算法处于相同区间,属于合理预期。对于单流场景,首秒重传数通常为零或极低,注入的甜品速度显著缩短了收敛时间,且未以增加重传为代价。

11. 测试数据

以下数据采集自特定实验环境,供同行复现参考。

  • 路径:中国成都移动(民用宽带)至美国洛杉矶
  • RTT:212 ms
  • 瓶颈带宽:1 Gbps(共享)
  • 并发流数:8
  • 丢包率:约 13.7%

稳态吞吐(8 流总和):

  • BBRv1:937 Mbps
  • KCC v1.0:1,010 Mbps

新流首秒吞吐(全局 Kalman 注入,折扣率 50%):

  • 无注入(纯冷启动):约 2.8 Mbps
  • 有注入:约 50 Mbps
  • 单流首秒重传计数:0 或极低;多流竞争下首秒重传计数与同类算法可比。

代码仓库:PPP PRIVATE NETWORK™ X --- Kalman Congestion Control v1.0 (KCC)

KCC v1.0 基于 BBRv1 状态机与卡尔曼滤波器构建。

12. 附图

附图(一):单流

附图(二):单流16M

附图(三):多流

附图(四):流媒体

稳态:
突发:
相关推荐
XBodhi.1 小时前
Visual Studio C++ 语法错误: 缺少“;”(在“return”的前面)
开发语言·c++·visual studio
pusheng20251 小时前
IFSJ全英文专访:中国创新力量重塑先进气体感知技术,赋能全球关键基础设施安全
前端·网络·人工智能·物联网·安全
Irissgwe2 小时前
五、应用层协议HTTP
linux·网络·网络协议·http·状态码·url
.千余2 小时前
【Linux】 传输层协议UDP:从端口号到传输机制
linux·运维·udp
囚~徒~3 小时前
轻量化的虚拟机
linux·运维·服务器
SteveSenna3 小时前
Ubuntu 20.04 安装 Isaac Sim 4.5 + Isaac Lab
linux·运维·服务器
froyoisle3 小时前
CSP-J 历年复赛 T1 及解析(2019~2025)
数据结构·c++·算法·csp-j·csp·算法竞赛·信息学
basketball6163 小时前
C++ 高级编程:2. 基本线程池实现
java·开发语言·c++
chao1898444 小时前
SGM(Semi-Global Matching)立体匹配算法 — C++ 实现
开发语言·c++·算法