带宽估计算法之BBR
此次,主要介绍一种效率更快的带宽探测策略。BBR主要的应用是在QUIC 协议里有支持。在webrtc里,也有一段时间是放到了正式版本中。此次,我就基于WEBRTC上的BBR版本和标准的bbr协议,来给大家做个大致的讲解。
BBR带宽估计 (Bottleneck Bandwidth and Round-trip propagation time)
参考文档:https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control
参考文档:https://www.ietf.org/proceedings/98/slides/slides-98-iccrg-an-update-on-bbr-congestion-control-00.pdf
一:BBR 的设计概述
-
网络路径模型
- BBR 是一种基于模型的拥塞控制算法:BBR 的模型包括两个显式估计参数
- BBR.BtlBw : bottleneck bandwidth(瓶颈带宽),根据最大发送数据率 获得
- BBR.RTprop :双向往返传播延迟,根据一个窗口内发送数据的最小RTT获得
- BBR 是一种基于模型的拥塞控制算法:BBR 的模型包括两个显式估计参数
-
目标工作点
- BBR 使用其模型寻找具有高吞吐量的操作点和低延迟。为了在最佳工作点附近工作(具有最大吞吐量和最小延迟的点)系统需要保持两个条件:
-
Rate balance: 最大发送速率
-
Full pipe:
BDP = BBR.BtlBw * BBR.RTprop
-
- BBR 使用其模型寻找具有高吞吐量的操作点和低延迟。为了在最佳工作点附近工作(具有最大吞吐量和最小延迟的点)系统需要保持两个条件:
-
控制参数
-
BBR 使用其模型来控制其发送行为,使保持在目标工作点附近。
1. pacing rate:BBR发送数据的速率 2. send quantum:发送数据量 3. cwnd:允许在飞行中的最大数据量。
-
-
状态机设计概述
-
BBR 希望通过一个简单的状态机改变其三个控制参数。
通过状态机,从而实现高吞吐量、低延迟的目标。 首先探测 BBR.BtlBw 然后探测 BBR.RTprop,通过顺序交替近似公平地共享带宽。 BBR 从 Startup 状态开始,并提高其发送速率 迅速地。当它估计管道已满时,它进入 Drain 状态以排空队列。在稳定状态下,BBR 仅使用 ProbeBW 状态 和(如果需要)ProbeRTT 状态 ProbeBW 状态:定期在飞行中短暂升高以探测更高的 BBR.BtlBw 样本 ProbeRTT 状态: 在飞行中短暂降低以探测较低的 BBR.RTprop 样本
-
4.1 状态转换图
下面的状态转移图总结了控制流程
|
V
+---> Startup ----+
| | |
| V |
| Drain ----+
| | |
| V |
+---> ProbeBW -----+
| ^ | |
| | | |
| +----+ |
| |
+---- ProbeRTT <---+
- 算法组织
二:通过代码来了解
RTT, LOSE RATE的统计::bandwidth_sampler.cc
-
计算实时发送码率
DataSize sent_delta = sent_packet.total_data_sent - sent_packet.total_data_sent_at_last_acked_packet; TimeDelta time_delta = sent_packet.sent_time - *sent_packet.last_acked_packet_sent_time; send_rate = sent_delta / time_delta;
-
计算实时反馈码率
DataSize ack_delta = total_data_acked_ - sent_packet.total_data_acked_at_the_last_acked_packet; TimeDelta time_delta = ack_time - *sent_packet.last_acked_packet_ack_time; DataRate ack_rate = ack_delta / time_delta;
-
计算实时bandwidth
BandwidthSample sample; sample.bandwidth = std::min(send_rate, ack_rate); max_bandwidth_.Update(sample.bandwidth, round_trip_count_);
-
计算实时rtt::
sample.rtt = ack_time - sent_packet.sent_time;
BBR的主要参数计算::bbr_network_controller.cc
带宽上探的过程
-
首先进入start up 快速上探模式
* mode = start up
* pacing gain = 2.885
* 连续3个RTT target_rate > 最大带宽,判断达到 最大带宽,is_at_full_bandwidth_= true。 -
然后进入 drain 模式,进行排空数据
* mode = drain
* pacing gain = 0.34662
* 当管道中的数据,小于一个1bdp时,认为排空结束。进入probe rtt 模式。
* msg.data_in_flight <= GetTargetCongestionWindow -
开始进入PROBE_BW模式,探测 带宽
* EnterProbeBandwidthMode :进入 probe bw 模式
* 通过UpdateGainCyclePhase来计算pacing gain。 -
稳定后的模式:
* pacer gain: 在1.25 ,1 , 0.75 中切换.
* 1.25 为探测带宽是否可以上溢。
* 0.75 为排空过程。
* 1 为带宽维持稳定的过程。
需要了解的参数:
-
计算实时loss rate::
-
这个是链路的真实丢包率,可以传到上层,给fec模块用来判断冗余的比例。
if(packets_sent > 0){ current_feedback_loss_rate_ = (float)((float)packets_lost/(float)packets_sent * 100); loss_rate_.UpdateWithLossStatus(msg.feedback_time.ms(), packets_sent, packets_lost);
-
-
计算pacing_gain::
if (round_offset == 0) return 1 + config_.probe_bw_pacing_gain_offset;// 1.25 else if (round_offset == 1) return 1 - config_.probe_bw_pacing_gain_offset;// 0.75 else return 1;
-
计算当前bandwidth:
-
这当网络没有丢包:bandwidth就是实际带宽。
-
在有丢包率的情况:当前总带宽=bandwidth/(1-loss rate).
实际透过率:这是服务器收到的feekback的数据,表示当前的可用带宽。 bandwidth = max_bandwidth_.GetBest()
-
-
计算pacing rate::
DataRate pacing_rate_ = pacing_gain_ * max_bandwidth_.GetBest();
-
计算cwd
DataSize bdp = GetMinRtt() * max_bandwidth_.GetBest(); DataSize congestion_window = gain * bdp;
三:与Paced Sender 的互动
-
Paced_sender 是一个数据平滑发送器,他的主要作用有两个:
* 在带宽允许的状态下,尽快的发送带宽。
* 如果有突发的数据,则要按间隔,分多次的发送,避免造成网络拥塞。
* Pacer 的发送模式:Pacer 内部有一个线程,定期(默认是5ms)的执行发送rtp 包的任务。 -
Pacer 中使用的参数
-
设置pacing和padding的 bitrate
* setPacingRates(uint32_t pacing_rate_bps, uint32_t padding_rate_bps * pacing_rate_bps 是最大的可发送码率,标明媒体数据可以发送的最大带宽。 * padding_rate_bps 是发送padding的最大码率,一般大于 等于pacing_rate_bps,当需要探测更高的带宽时,这时 媒体信源的码率不能提升,所以只能通过发送空的padding包来填充管道,来探测是否能够达到更高的带宽。
-
设置pacer 增益
* pacing_gain_ = pacing_gain; * 在带宽稳定的的情况下gain的值为1 。只有系统希望多发一些带宽,去探测一下当前是否有更多的带宽可以用时,会希望超发一些带宽,这个时候,Pacing gain 会 > 1. * 当然,当系统超发后造成的管道拥塞,就会把gain 小于1,此时会开始排空管道,减少拥塞。
-
设置拥塞窗口
* data_in_fly(管道中的数据) = data_by_send - data_by_ack * congestion_window_bytes_ = congestion_window_bytes * pacing_factor_ * 当data_in_fly(管道中的数据) > congestion_window_bytes_ 时,表示拥塞了,Pacer 需要停下来, 等待data_in_fly 下降到小于congestion_window_bytes_时,才能重新发送数据。
-
-
Pacer的优化方向:
* 在pacer 中多加几个rtp队列,可以精准的控制媒体流的发送顺序。
* 对nack,media,audio进行区别对待。
* padding包的发送,可以使用媒体包来发送,从而达到加冗余的效果,提高抗弱网的性能。