• BBR 带宽估计的延后累加


    一个关于时延统计分布的小测试,用 netem delay jitter distribution pareto 模拟,得到下面的结果:
    在这里插入图片描述

    netem 的 jitter 并不是真 jitter,只是通过延时阻滞部分报文模拟 jitter,对保序流而言,就表现为乱序,如下图所示,通过 ack 号和时间戳均能看出:
    在这里插入图片描述

    netem 的局限有目共睹,这里不纠结。问题是,如果现实中真的发生 ack 乱序会怎样。

    Linux kernel TCP 单独处理了 old_ack:

    if (before(ack, prior_snd_una)) {
            goto old_ack;
    }
    ...
    old_ack:
            /* If data was SACKed, tag it and see if we should send more data.
             * If data was DSACKed, see if we can undo a cwnd reduction.
             */
            if (TCP_SKB_CB(skb)->sacked) {
                    flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
                                                    &sack_state);
                    tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                          &rexmit);
                    tcp_newly_delivered(sk, delivered, flag);
                    tcp_xmit_recovery(sk, rexmit);
            }
    
            return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注意 tcp_sacktag_write_queue 调用,它会将 sack_block in old_ack 累加到当前 tp->delivered 字段,累加过去的 sack_block 会过估当前 delivery rate。

    这是一个明确的 ack 乱序导致的 tp->delivered 延后累加,先再次解释 delivery rate 的计算方法:

    • 找到当前 ack 中被 ack/sack 的最晚发送的报文 p;
    • 确认 p 被发送的时间 t_p 及当时成功 delivered 的数据量 n_p;
    • 计算速率 r = (n_curr - n_p) / (t_curr - t_p)

    整件事可用下面的 tcptrace 时空图解释:
    在这里插入图片描述

    当 old_ack 到达时发送的报文被 ack 后,迟到累加的 tp->delivered 对带宽的过估影响才能消除。否则在 sack_block in old_ack 的实际核算区域,计算 delivery rate 时均要减去本 sack_block 覆盖的数据量,由于 old_ack 迟到造成的此前带宽低估就随它去吧。

    这个修改背后的思想是合理的,任何时间序列(而不是空间)中,历史的误判不应让未来买单,要及时止损,否则可能面临双倍惩罚。如果前面一个 rtt 低估了带宽,低估就低估吧,不要以下一个 rtt 高估带宽来弥补。

    像 bbr 类 rate-based 算法,低估带宽不可怕,它自己的状态机(比如 probe up)能搞定,但高估带宽甚至直接打乱状态机的正常运行,比如 buffer 以非预期方式被占用,竟然因为高估带宽而不是 probe up,则 0.75x drain 就没用了,就像飞机失速很难改出一样。

    简单的例子,bbr 流故意高估带宽,带来更大的 delivery rate 被记住,几乎没有任何机制排空 buffer,这是个相当不稳定的状态。如果抖动真由 buffer 带入,很容易由于瞬时 minrtt 造成带宽高估(这也是 netem 靠乱序模拟抖动时吞吐更高的原因,但凡有几个极小的 rtt 就赢了),将状态机引入难以控制的未知。

    本文内容实在想不通就考虑 old_ack 丢了,但丝毫不影响传输的场景,丢就丢了呗,为什么要为过去的事揪心呢,迟到了就是晚了,就当它丢了得了。为过去的事,搅乱当前的安排(schedule),不值当,那就干脆点,全部干掉好了,广义地说,maybe undo 和 maybe retrans 也不要才好,那就把 old_ack 这个 label 去掉吧。

    皮鞋没有蹬上,露着白袜子。

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

  • 相关阅读:
    上海亚商投顾:三大指数小幅下跌 光刻机概念股午后走强
    java批量消费队列(BlockingQueue)中的数据
    Gradle笔记 七 publishing 项目发布
    02-CSS属性:背景属性
    PLC中ST编程——单按钮控制多台电机顺序启动
    SpringBoot 整合Thymeleaf教程及使用
    SpringBoot SpringBoot 原理篇 1 自动配置 1.17 自动配置原理【3】
    SpringMVC概述与简单使用
    Flutter splash 屏幕
    【算法-数组3】螺旋数组(一入循环深似海啊!)
  • 原文地址:https://blog.csdn.net/dog250/article/details/132764503