网络带宽使用状态自适应阈值的设计,下面我们就来说一说关于判断网络拥塞的依据是?我们一起去了解并探讨一下这个问题吧!

判断网络拥塞的依据是(WebRTC拥塞控制)

判断网络拥塞的依据是

目录:

网络带宽使用状态

自适应阈值的设计

  • 为什么要动态自适应?

  • 阈值自适应算法

    源码分析

  • Detect 函数

  • UpdateThreshold 函数

    导读

    上一篇介绍了 WebRTC 拥塞控制 | Trendline 滤波器计算延迟梯度趋势的过程,求得了最终值 trendline_slope。接下来,就要用这个斜率值与阈值进行比较,从而检测网络带宽的使用状态,比如是否过载。实际的带宽检测过程涉及到:调整延迟梯度斜率值、过载触发条件、阈值自适应,本篇将介绍这个过程。

    网络带宽使用状态

    WebRTC 定义了三种网络带宽的使用状态:Normal、Underuse、Overuse,即正常、低载、过载。

    enum class BandwidthUsage { kBwNormal = 0, kBwUnderusing = 1, kBwOverusing = 2, };

    过载检测原理

    自适应阈值的设计为什么要动态自适应?
    1. 使用太大的阈值

  • ,那么当检测到过载信号时队列延迟可能已经很大了,或者根本检测不到网络拥塞,算法不够灵敏。

  • 使用太小的阈值

    ,对于很小的延迟梯度趋势

  • 的增长,可能会被判定为网络带宽过载,使得发送码率降低,算法过于敏感。

  • 固定的阈值会导致与 TCP(采用基于丢包的拥塞控制)的竞争中被饿死,要保证内部协议公平性(Intra-protocol fairness)。

    理想的网络环境中,延迟梯度为 0,而在实际的网络环境中,延迟梯度则是不断变化的,让阈值跟随延迟梯度的变化而进行动态调整,可以降低 GCC 算法对延迟梯度变化的敏感度。

    C 音视频开发学习资料:点击领取→音视频开发(资料文档 视频教程 面试题)(FFmpeg WebRTC RTMP RTSP HLS RTP)

    阈值自适应算法

    GCC 提出的阈值自适应算法公式如下:

    源码分析

    基于 WebRTC 71 版本。

    网络带宽的过载检测与动态阈值的更新也是在 TrendlineEstimator 类中实现的,函数声明如下:

    class TrendlineEstimator { private: void Detect(double trend, double ts_delta, int64_t now_ms); void UpdateThreshold( double modified_trend, int64_t now_ms); };

    每个数据包组(除了首个包组)的到来都会触发过载检测和阈值的动态更新。

    Detect 函数

    该函数输入延迟梯度趋势的值、包组的发送时间差、包组的到达时间,从而评估网络带宽的使用状态,比如是否过载?

  • 对延迟梯度趋势斜率值

    进行调整。

    constexpr int kMinNumDeltas = 60; const double modified_trend = std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;

    num_of_deltas_ 表示包组间延迟梯度计算的次数,取值范围是 [2, 60],threshold_gain_ 是 Trendline 滤波器的增益参数,其默认值为 4,这两个变量都会对延迟梯度趋势值进行放大。

    if (modified_trend > threshold_) { if (time_over_using_ == -1) { time_over_using_ = ts_delta / 2; } else { time_over_using_ = ts_delta; } overuse_counter_ ; if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) { if (trend >= prev_trend_) { time_over_using_ = 0; overuse_counter_ = 0; hypothesis_ = BandwidthUsage::kBwOverusing; } } }

    值得一提的是,源码关于过载时间的计算是:过载时长等于包组发送时间差值send_delta_ms的累加。但是在首次检测到过载时,过载时长会初始化为包组发送时间差的一半,我把这个做法理解为一种类似于 TCP 的慢启动策略。注意,在满足所有条件触发过载信号后,过载时长与过载次数这两个变量要重置为 0。

    UpdateThreshold 函数

    该函数动态更新延迟梯度趋势的阈值。

    当延迟梯度斜率和阈值的差值大于 kMaxAdaptOffsetMs(15) 时,不更新阈值。

    if (fabs(modified_trend) > threshold_ kMaxAdaptOffsetMs) { last_update_ms_ = now_ms; return; }

    这种情况可能发生在这样的场景:因为某些原因,网络链路的容量突然降低,导致延迟梯度瞬间急剧增长。

    int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs); threshold_ = k * (fabs(modified_trend) - threshold_) * time_delta_ms; threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);

    还有两点值得一提:

    1. 阈值计算公式中的 time_delta_ms 指的是包组的到达时间差arrival_delta_ms,而上文中过载时长则是根据包组的发送时间差send_delta_ms来计算。

    2. 初始阈值设置为 12.5,计算后的阈值需要控制到 [6, 600] 这个区间内。GCC 草案解释了这么做的原因:

    since a too small del_var_th(i) can cause the detector to become overly sensitive.

    测试用例

    继续使用了上篇的测试用例,一共构造了 41 个包组,来模拟过载检测的过程,输出如下:

    测试输出

    观察日志输出,有几个比较关键的点:

    1. 初始的延迟梯度阈值设置为 12.5,随后开始动态自适应,一直调整到了 GCC 草案建议的阈值范围的最小值 6,在包组 21 到来并开始计算延迟梯度斜率之前,保持不变。

    2. 包组 21 到来之前,样本点数量未达到窗口大小,虽然不会进行延迟梯度斜率的计算,但是会执行过载检测和动态阈值更新,由于斜率初始化为 0,小于阈值 6,故认为网络状态正常。

    3. 包组 21 到来之后,样本点达到窗口大小 20,开始计算延迟梯度斜率,可以看出,阈值跟随斜率而变动,但由于斜率一直大于阈值,故网络一直处于过载状态。