网络带宽_WebRTC 拥塞控制 | 网络带宽过载检测

本文是 WebRTC 拥塞控制3

  • 导读

  • 网络带宽使用状态

  • 自适应阈值的设计

    • 为什么要动态自适应?

    • 阈值自适应算法

  • 源码分析

    • Detect 函数

    • UpdateThreshold 函数

  • 测试用例

导读

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

网络带宽使用状态

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

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

下图展示了过载检测中三种信号的产生机制,其中,上下两条红色曲线表示动态阈值 ,蓝色曲线表示调整后的延迟梯度斜率值 。

21e32d2068d7c72a8efd0f7c31a81137.png
过载检测原理

可知,网络带宽使用状态的判定方法为:

  • > ,overuse
  • < -,underuse
  • -,normal

自适应阈值的设计

为什么要动态自适应?

  1. 使用太大的阈值 ,那么当检测到过载信号时队列延迟可能已经很大了,或者根本检测不到网络拥塞,算法不够灵敏。
  2. 使用太小的阈值 ,对于很小的延迟梯度趋势  的增长,可能会被判定为网络带宽过载,使得发送码率降低,算法过于敏感。
  3. 固定的阈值会导致与 TCP(采用基于丢包的拥塞控制)的竞争中被饿死,要保证内部协议公平性(Intra-protocol fairness)。

比如,在 GCC 流与 TCP 流共存的场景下,延迟梯度 ,如果 ,那么 GCC 流会受到 TCP 流的过高的延迟梯度的影响,连续触发过载信号,降低自己的发送码率,导致 GCC 流的接收者被 "饿死"。Gcc Analysis[1] 解释了这个问题:

adaptive threshold is able to solve the starvation issue and allows the GCC flow to share the bottleneck fairly with the concurrent TCP flow,γ(t) follows m(t) with a smaller time constant which avoids the generation of a large number of consecutive overuse signals and prevents the starvation of the GCC flow.

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

阈值自适应算法

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

其中,,代表包组到达时间差,即距上一次阈值更新的时间差。  代表阈值的增长率(有可能负增长),增长的基准值是当前调整后的延迟梯度趋势值与当前阈值的差  - 。

系数按如下公式取值:

和  决定了阈值的减少和增加的速度。当延迟梯度斜率值  在阈值范围  内时,要降低阈值,否则,要升高阈值。总之,阈值动态调整的原则是: 随  而动

GCC 草案[2] 建议  和  的取值分别是 0.00018 和 0.01,即阈值的增长速率要大于阈值的降低速率。上文已经解释了这样做的目的:降低过载检测算法的敏感度,保证在和 TCP 的竞争中的公平性。

源码分析

基于 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,这两个变量都会对延迟梯度趋势值进行放大。

这里的关键就在于为何要放大 ?

是一个斜率值,取值范围 (-1, 1),这个值很小,而阈值  要跟随  的变化而变化,这可能导致  很容易的超过  从而触发连续的过载信号。放大  可以使得算法不会因为  的波动而过于敏感。

  • 过载信号触发条件
  1. 延迟梯度斜率值 > 当前阈值
  2. 过载总时长 > kOverUsingTimeThreshold(10ms)
  3. 过载次数   1
  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;
}

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

接下来就是新阈值的计算过程了,完全参考公式 (1)。注意,WebRTC 对  和  的取值分别是 0.0087 和 0.039,并非 GCC 草案的建议值。

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 个包组,来模拟过载检测的过程,输出如下:

e47ae9973ec08a3d864578ad68164321.png
测试输出

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

  1. 初始的延迟梯度阈值设置为 12.5,随后开始动态自适应,一直调整到了 GCC 草案建议的阈值范围的最小值 6,在包组 21 到来并开始计算延迟梯度斜率之前,保持不变。
  2. 包组 21 到来之前,样本点数量未达到窗口大小,虽然不会进行延迟梯度斜率的计算,但是会执行过载检测和动态阈值更新,由于斜率初始化为 0,小于阈值 6,故认为网络状态正常。
  3. 包组 21 到来之后,样本点达到窗口大小 20,开始计算延迟梯度斜率,可以看出,阈值跟随斜率而变动,但由于斜率一直大于阈值,故网络一直处于过载状态。

至此,网络带宽过载检测的内容介绍完毕。下一篇将介绍三种网络带宽使用状态的信号(normal、overuse、underuse)如何驱动 AIMD 码率控制器工作,从而有效的进行拥塞控制,感谢阅读。

参考资料

[1]

Gcc Analysis: https://c3lab.poliba.it/images/6/65/Gcc-analysis.pdf

[2]

GCC 草案: https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.4


                            码神说2da76af00fe13cee13c5275c92169c5d.png

                   一个有故事的程序员7bae4d2fb8712dbf51aba2c6211ce952.png

                   坚持有诚意的技术原创9bf094df0e1c0a5ce6dfe6519e11bf02.png

1584ace0ec9fc39d932dbdfbddc7a7e8.png

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值