网络带宽使用状态自适应阈值的设计,下面我们就来说一说关于判断网络拥塞的依据是?我们一起去了解并探讨一下这个问题吧!
判断网络拥塞的依据是
目录:网络带宽使用状态
自适应阈值的设计
源码分析
上一篇介绍了 WebRTC 拥塞控制 | Trendline 滤波器计算延迟梯度趋势的过程,求得了最终值 trendline_slope。接下来,就要用这个斜率值与阈值进行比较,从而检测网络带宽的使用状态,比如是否过载。实际的带宽检测过程涉及到:调整延迟梯度斜率值、过载触发条件、阈值自适应,本篇将介绍这个过程。
网络带宽使用状态WebRTC 定义了三种网络带宽的使用状态:Normal、Underuse、Overuse,即正常、低载、过载。
enum class BandwidthUsage {
kBwNormal = 0,
kBwUnderusing = 1,
kBwOverusing = 2,
};
过载检测原理
自适应阈值的设计为什么要动态自适应?- 使用太大的阈值
,对于很小的延迟梯度趋势
理想的网络环境中,延迟梯度为 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);
还有两点值得一提:
- 阈值计算公式中的 time_delta_ms 指的是包组的到达时间差arrival_delta_ms,而上文中过载时长则是根据包组的发送时间差send_delta_ms来计算。
- 初始阈值设置为 12.5,计算后的阈值需要控制到 [6, 600] 这个区间内。GCC 草案解释了这么做的原因:
测试用例since a too small del_var_th(i) can cause the detector to become overly sensitive.
继续使用了上篇的测试用例,一共构造了 41 个包组,来模拟过载检测的过程,输出如下:
测试输出
观察日志输出,有几个比较关键的点:
- 初始的延迟梯度阈值设置为 12.5,随后开始动态自适应,一直调整到了 GCC 草案建议的阈值范围的最小值 6,在包组 21 到来并开始计算延迟梯度斜率之前,保持不变。
- 包组 21 到来之前,样本点数量未达到窗口大小,虽然不会进行延迟梯度斜率的计算,但是会执行过载检测和动态阈值更新,由于斜率初始化为 0,小于阈值 6,故认为网络状态正常。
- 包组 21 到来之后,样本点达到窗口大小 20,开始计算延迟梯度斜率,可以看出,阈值跟随斜率而变动,但由于斜率一直大于阈值,故网络一直处于过载状态。