随着Internet的发展,网络中出现了愈来愈多的高速和长距离链路,这些链路的特色是时延带宽积(BDP=bandwith*RTT)很大,也就是说,这些链路所能容纳的总数据量很大。linux
传统TCP协议,例如TCP-Reno、TCP-NewReno、TCP-SACK中,每过一个RTT(Round Trip Times),窗口增长一个单位,这使得TCP的数据传输速度缓慢,远不能充分利用网络带宽。当碰上高带宽环境时,可能须要经历不少个RTT,拥塞窗口才能接近于一个BDP。若是数据流很短,可能拥塞窗口还没增加到一个BDP,数据流就已经结束了,这种状况的带宽利用率就会很是低。c++
假设网络带宽是10Gbps,RTT是100ms,数据包是固定的1250字节,则此网络所能容纳的数据包总量是:\(\dfrac{10\times 10^9\times 0.1}{1250\times 8}=10^5\)个数据包。假设窗口从50000开始增加,也须要50000个RTT(1.4个小时)才能达到网路的满负荷,若是一个TCP流在此以前结束(显然每每如此),则未充分利用带宽。算法
BIC-TCP采用二分搜索的方式来决定拥塞窗口的增加尺度,首先它会记录拥塞窗口的一个最大值点,这个最大值就是TCP最近一次出现丢包时拥塞窗口的值;还会记录一个最小值点,即在一个RTT周期内没有出现丢包事件时窗口的大小。二分搜索就是取最小值和最大值的中间点,当拥塞窗口增加到这个中间值且没有出现丢包的话,就说明网络还能够容纳更多的数据包。那么将这个中值设为新的最小值,在新的最小值和最大值间搜索中间值。当前拥塞窗口的值还远没有达到通道的容量时,其增加速度很快;相反,当拥塞窗口的值接近于通道的容量时,其拥塞窗口增加函数是一个简化的对数凸函数。这个凸函数使拥塞窗口在饱和点或平衡点比凹函数或线性函数保持更长的时间,在饱和点处,凸函数和线性函数具备最大的窗口增量,所以在丢包发生时会出现大量的数据包被丢失。网络
BIC-TCP的主要特征是在前面说过的其独特的窗口增加函数,图1给出了BIC-TCP的窗口增加函数。当出现丢包事件时,BIC-TCP经过乘以因子 \(\beta\) 来缩小窗口,缩小以前的窗口大小被设置为最大值\(W_{\max}\) ,而且缩小以后的窗口大小被设置为最小值\(W_{\min}\) 。 而后,BIC-TCP使用这两个参数执行二分搜索,拥塞窗口的下一个取值会是\(W_{\max}\) 和\(W_{\min}\) 之间的“中点” \(W_{mid}\) 。app
为了防止拥塞窗口从\(W_{\min}\) 增加到 \(W_{\max}\) 的步长step太大,BIC-TCP还设置了一个常数\(S_{\max}\) ,当step>\(S_{\max}\) 时,BIC-TCP会取下一个增加点为 \(W_{\min}\)+\(S_{\max}\) 而不是 \(W_{mid}\) (加法增长阶段),若是没有出现丢包的话,再更新\(W_{\min}\) ,直到step< \(S_{\max}\) 为止。与此同时BIC-TCP还设置一个另外一个控制参数\(S_{\min}\) ,当窗口增量小于 \(S_{\min}\) 时,BIC-TCP会将当前拥塞窗口值设为最大值。异步
若是窗口增加超过最大值,则说明当前窗口最大值还不是一个饱和点,网络还能够容纳更多的数据包,窗口还有增加的空间,一个新的窗口最大值须要被探索。因而BIC-TCP会进入一个新的阶段,叫作最大值探索阶段。最大探测使用一个与在加法增加和二分搜索阶段彻底对称的窗口增加函数。图1中给出了在最大探索阶段期间的窗口增加函数。在最大探测期间,窗口最初缓慢地增加以发现附近新的最大值,通过一段时间的缓慢增加,若是没有找到新的最大值(即没出现包丢失),则它猜想新的最大值离得很远,因此它给窗口大小增长一个大的固定增量,使用加法增长切换到更快的增长速度。BIC-TCP的良好性能来自 \(W_{\max}\) 附近的缓慢增长以及在加法增长和最大探测期间的线性增长。tcp
BIC-TCP在高速网络中具备良好的可扩展性、多个流竞争的公平性和低窗口振荡的稳定性。然而,BIC-TCP的增加功能对于TCP来讲仍然过于激进,特别是在短RTT或低速网络下。此外,窗口控制的几个不一样阶段(二进制搜索增长、最大探测、\(S_{\max}\) 和 \(S_{\min}\) )增长了协议实现和性能分析的复杂性。ide
CUBIC是BIC-TCP的下一代版本。 它经过用三次函数(包含凹和凸部分)代替BIC-TCP的凹凸窗口生长部分,大大简化了BIC-TCP的窗口调整算法, 该函数在保留BIC-TCP的优势(特别是其稳定性和可扩展性)的同时,简化了窗口控制,加强了其TCP友好性。实际上,任何奇数阶多项式函数都具备这种形状。三次函数的选择是偶然的,可是也是出于方便。CUBIC的关键特征是其窗口增加仅取决于两个连续拥塞事件之间的时间。一个拥塞事件是指出现TCP快速恢复的时间。所以,窗口增加与RTT无关。 这个特性容许CUBIC流在同一个瓶颈中竞争,有相同的窗口大小,而不依赖于它们的RTT,从而得到良好的RTT公平性。并且,当RTT较短时,因为窗口增加率是固定的,其增加速度可能比TCP标准慢。 因为TCP标准(例如,TCP-SACK)在短RTT下工做良好,所以该特征加强了协议的TCP友好性。函数
CUBIC的窗口增加函数是一个三次函数,很是相似于BIC-TCP的窗口增加函数,CUBIC的函数图像如图2所示。CUBIC的详细运行过程以下,当出现丢包事件时,CUBIC同BIC-TCP同样,会记录这时的拥塞窗口大小做为\(W_{\max}\),接着经过常数因子\(\beta\) 执行拥塞窗口的乘法减少,这里 \(\beta\) 是一个窗口下降常数,并进行正常的TCP快速恢复和重传。从快速恢复阶段进入拥塞避免后,使用三次函数的凸函数增长窗口。三次函数设置在 \(W_{\max}\) 处达到稳定点,若是存在新的最大窗口(网络带宽发生变化), 而后使用三次函数的凹函数开始探索新的最大窗口。源码分析
CUBIC的窗口增加函数公式以下所示:
\(C\)是一个CUBIC的参数, \(t\)是从窗口上次下降开始到如今的时间,是一个弹性值,而 \(K\) 是上述函数在没有进一步丢包的状况下将当前的拥塞窗口 \(W\) 增长到 \(W_{\max}\) 经历的时间。
\(K\)计算公式以下:
在拥塞避免阶段每收到一个ACK,CUBIC都会使用方程(1)计算在下个RTT的窗口增加速率。CUBIC使用$W\left( t+RTT \right) $做为拥塞窗口的候选值,假设当前拥塞窗口大小为 \(cwnd\)。根据 \(cwnd\)的值,CUBIC有三种运行模式。在linux内核实现中,\(K\)值立方根求取使用得是牛顿迭代法,这是由于牛顿迭代法的性能优于二分法。
若是\(cwnd\) 小于(标准)TCP在上次丢包事件以后 \(t\) 时刻到达的窗口大小,那么CUBIC处于TCP模式(咱们将在下面描述如何根据时间肯定标准TCP的窗口大小)。
若是\(cwnd\) 小于\(W_{\max}\) ,那么CUBIC在三次函数的凸函数区域。
若是\(cwnd\) 大于\(W_{\max}\) ,那么CUBIC处于三次函数的凹区域。
标准TCP协议在网络时延带宽积小或者RTT小的状况下表现仍不错,CUBIC被设计为在这两种状况下能够很好的兼容标准TCP协议。算法执行过程当中,每收到一个ACK后都会判断当前是否处于标准TCP阶段,即TCP友好域,以此来更好的兼容TCP。所以须要经过TCP的AIMD(加法增、乘法减)特性并使用加法因子 $\alpha $ 和乘法因子 \(\beta\) 去估算在TCP传统拥塞算法下的拥塞窗口大小。
\(W_{\max}\left( 1-\beta \right)\)是当发生拥塞事件时减小后的初始 \(cwnd\), \(3\frac{\beta}{2-\beta}\)为线性增加的大小(斜率), \(\frac{t}{RTT}\)为从发生拥塞窗口到至今须要经历RTT的个数。若是当前的 \(cwnd\) 小于\(W_{tcp\left( t \right)}\) ,则处于TCP模式,所以每次接受ACK时,都会将 \(cwnd\)设置为\(W_{tcp\left( t \right)}\) 。
当在拥塞避免阶段收到一个ACK,若是协议不处于TCP模式,且\(cwnd\)小于\(W_{\max}\),那么协议就处于凸区域,在这个区域,\(cwnd\) 的增量为:
当前的\(cwnd\) 大于\(W_{\max}\) 时,协议就会进入凹区域。因为 \(cwnd\)大于先前的饱和点\(W_{\max}\) ,这代表自上次拥塞事件以来,网络条件可能受到干扰,这可能意味着在一些竞争流离开后,可用带宽会增长。因为网络是高度异步的,可用带宽的波动老是存在的。凹区域使得窗口在开始时增加很是缓慢,并逐渐增长其增加率。因为CUBIC正在搜索一个新的\(W_{\max}\) ,咱们也将此阶段称为最大探测阶段。因为没有修改凹区域的窗口增加函数,所以两个区域的窗口增加函数保持不变。所以 \(cwnd\)的增量一样为:
当出现数据包丢失时,CUBIC会经过乘法因子 $\beta $ 来下降拥塞窗口,这里取\(\beta =0.2\) 。虽然自适应性的设置 $\beta $ 会致使更快的收敛,可是会使协议的分析变得更加困难,并影响协议的稳定性。
新的流量加入网络时,网络中的现有流量须要放弃其部分带宽份额,以使新流量有必定的增加空间。在发生丢包前,CUBIC会记录一个最大窗口值\(W_{\max}\) 。当发生丢包后,在下降窗口前,CUBIC又会记录当前的窗口值做为新的\(W_{\max}\) ,为了避免至于混淆,能够将以前记录的\(W_{\max}\) 记为\(W_{last\_max}\)。当发生丢包时,CUBIC会比较$ W_{last_max}$ 和\(W_{\max}\) 的大小,若是\(W_{\max}\) 小于$ W_{last_max}$ ,这代表因为可用带宽的变化,该流的窗口饱和点正在下降。这种状况下,CUBIC的作法是经过进一步的减少\(W_{\max}\) 来释放更多的可用带宽,使得新加入的流量有必定的增加空间。
对所须要的参数进行初始化,其中tcp_friendliness决定TCP友好型区域是否开启,默认为开启状态;fast_convergence决定快速收敛机制是否开启,默认为开启状态;其他的参数值须要放入cubic_reset()函数中,由于这些参数在每次发生拥塞时间后,都须要进行重置。其中 \(W_{last\_max}\) 为上一次发生拥塞窗口时所记录的窗口值;epoch_start会记录每次发生拥塞的时间;origin_point则记录\(W_{last\_max}\) 和当前的 \(cwnd\) 之间的最大值;dMin记录最小的RTT(往返传输时间); \(W_{tcp}\)为TCP模式时的计算得出的窗口值; \(K\)是在没有进一步丢包的状况下将当前的拥塞窗口 \(W\)增长到\(W_{\max}\) 经历的时间;ack_cnt 记录接收到ACK的数量。
每当接受到一个ACK时,dMin会去记录自身与当前RTT的最小值,保证dMin记录着最小的RTT,这样作的缘由是因为网络的不肯定性因素,致使每一个ACK对应的RTT可能也会有增长,所以须要找到最接近实际的RTT。ssthresh判断当前状态为慢启动仍是拥塞避免阶段的阈值,若是当前的cwnd小于或等于ssthresh,那么进入慢启动阶段,每接收到一个ACK,cwnd相应自加一,直到cwnd大于ssthresh,则须要进入拥塞避免阶段,cubic_update()函数会计算得出一个cnt值,与cwnd_cnt进行比较,以此判断cwnd是否须要增加。
当包丢失(拥塞事件)时,经过快速恢复阶段,接下来就会从新进入拥塞避免。所以epoch_start须要置为0。若是发生拥塞事件时的cwnd小于上一次发生拥塞事件的\(W_{last\_max}\) ,而且快速收敛机制为开启状态,即意味着是因为新流的加入,致使cwnd小于\(W_{last\_max}\) ,所以须要将当前的cwnd须要让出一部分带宽给新流;不然就是因为网络的波动(带宽减小或增长)致使cwnd小于或大于\(W_{last\_max}\) ,那么只须要将当前的cwnd记录到\(W_{last\_max}\) 便可。当cwnd和ssthresh进行乘法减小后,从新开始拥塞避免阶段(即凸函数区域)
Cubic_update()函数为核心算法的实现。当epoch_start为零时,意味着从新进入拥塞避免后阶段,若是cwnd小于\(W_{last\_max}\),须要计算cwnd到达\(W_{last\_max}\) 的时间K;反之若是cwnd大于\(W_{last\_max}\) ,意味着网络的带宽增长了,须要进行窗口最大值探索(凹函数区域)。时间\(t\)记录的是以拥塞避免阶段为起点时间到当前的时间之差,然后与dMin之和,于是去计算出下一个dMin所须要到达的目标窗口值target。若是target大于当前窗口值cwnd,须要增长窗口的增加率;反之,意味着当前窗口值已经到达平稳期,须要去下降窗口的增加率。
同时,也须要计算若是是在传统的TCP拥塞控制算法下的窗口值\(W_{tcp}\) ,而后\(W_{tcp}\) 与在CUBIC拥塞控制算法下的cwnd进行比较,以此判断是否进入tcp友好型区域。这样经过与TCP传统拥塞控制算法的组合,可以获得更高的网络质量。
从做者的角度来看,是如何从经过改进BIC算法获得CUBIC算法,最主要的仍是窗口增加函数的变化。
从上图,能够看出该函数的斜率是从高到低,然后又慢慢增大。所以,能够直观假设其导数为二次曲线函数,以\(g\left( x \right) =3x^2-2x\)为例,它的图像以下图所示。
由此,能够获得它的原函数为\(f\left( x \right) =x^3-x^2+1\) ,即它的图像为
以上仅仅是个例子,目的在于肯定CUBIC的曲线形状,上面的图像和BIC的窗口探测曲线很像,在肯定了曲线的形状以后,最终要肯定曲线的参数,而曲线的通用方程应该是:
对于参数的肯定,须要联系CUBIC拥塞控制算法的过程,\(W_{\min}\)实际上就是上次拥塞事件发生后最大窗口( \(W_{\max}\) 记录着拥塞事件发生时所测量出的对应的最大窗口值,即为已知)乘法减小后的值,所以 \(W_{\min}=W_{\max}\times beta\),再者因为函数的对称特性,可以计算得出最高点值为$W_{\max}+W_{\max}\left( 1-beta \right) $ 。
所以,能够得出如下的图像,因为这个函数增加图像对应着一次拥塞避免状态的从新开始,所以P1点处的时间为零,而由P1到P0的时间假设为r已知,根据对称的特性,于是P2处的时间也为2r。
因为曲线自己是关于P0(x=r, y=\(W_{\max}\) )这个点对称的,所以能够肯定曲线方程的常数因子为\(W_{\max}\) ,因此可以将曲线方程写成以下的形式:
如今的目标就是求h(x),再因为P1和P2点已知:
由0、一、3式可得:
根据4式以及f(x) ,即h(x)为三次函数的特性,能够假设:
其中C为一常量,所以咱们须要将5式代入6式中,可得:
综上可得:
上图为在TCP传统拥塞控制算法(New-Reno)下,窗口值随时间的变化过程,其中W对应着发生拥塞事件时的窗口值(最大窗口值),因为上图具备周期性,所以只需对第一个周期的图像进行分析便可,在发生拥塞事件后,须要对\(W\)进行乘法减小,即\(\left( 1-b \right) W\)(起点),然后经过TCP的累计确认机制以固定的速率a去进行窗口值的增加,直到到达W。
显然,经过等差数列公式,能够得出:
又该等差数列应该从零开始,因此
\(\left( 1-b \right) W\)须要n次RTT才能到达\(W\) ,所以在这一个周期内的平均窗口值\(S\),应该拿窗口值总和,除以迭代增加的次数\(n\)。
假设RTT为R时间(second),所以每秒的发包速率为:
假设在这个一个周期内,发送窗口的总和为P,则
所以丢包率(丢一个包时)p为
由此可得
而后,再将8式代入5式获得每秒的平均发包速率为
又在TCP传统得拥塞控制算法中,\(\alpha =1\) \(\beta =0.5\) , \(\hat{T}=\frac{1}{R}\sqrt{\frac{3}{2}\frac{1}{p}}\) 。所以在CUBIC算法中保持这种关系,只有当\(\alpha =\frac{3\beta}{2-\beta}\) 时知足,且\(\alpha\) 为TCP拥塞控制算法窗口增长的加法因子,因此在CUBIC中计算 \(W_{tcp}\)的公式为:
/* * Compute congestion window to use. */ //从快速恢复退出并进入拥塞避免状态以后,更新cnt static inline void bictcp_update(struct bictcp *ca, u32 cwnd) { u64 offs;//时间差|t - K| //delta是cwnd差,bic_target是预测值,t为预测时间 u32 delta, t, bic_target, max_cnt; ca->ack_cnt++; /*ack包计数器加1 count the number of ACKs */ if (ca->last_cwnd == cwnd && //当前窗口与历史窗口相同 (s32)(tcp_time_stamp - ca->last_time) <= HZ / 32)//时间差小于1000/32ms return; //直接结束 ca->last_cwnd = cwnd;//记录进入拥塞避免时的窗口值 ca->last_time = tcp_time_stamp;//记录进入拥塞避免时的时刻 if (ca->epoch_start == 0) {//丢包后,开启一个新的时段 ca->epoch_start = tcp_time_stamp; /*新时段的开始 record the beginning of an epoch */ ca->ack_cnt = 1; /*ack包计数器初始化 start counting */ ca->tcp_cwnd = cwnd; /*同步更新 syn with cubic */ //取max(last_max_cwnd , cwnd)做为当前Wmax饱和点 if (ca->last_max_cwnd <= cwnd) { ca->bic_K = 0; ca->bic_origin_point = cwnd; } else { /* Compute new K based on * (wmax-cwnd) * (srtt>>3 / HZ) / c * 2^(3*bictcp_HZ) */ ca->bic_K = cubic_root(cube_factor * (ca->last_max_cwnd - cwnd)); ca->bic_origin_point = ca->last_max_cwnd; } } /* cubic function - calc*/ /* calculate c * time^3 / rtt, * while considering overflow in calculation of time^3 * (so time^3 is done by using 64 bit) * and without the support of division of 64bit numbers * (so all divisions are done by using 32 bit) * also NOTE the unit of those veriables * time = (t - K) / 2^bictcp_HZ * c = bic_scale >> 10 == 0.04 * rtt = (srtt >> 3) / HZ * !!! The following code does not have overflow problems, * if the cwnd < 1 million packets !!! */ /* change the unit from HZ to bictcp_HZ */ t = ((tcp_time_stamp + (ca->delay_min>>3) - ca->epoch_start) << BICTCP_HZ) / HZ; //求| t - bic_K | if (t < ca->bic_K) // 还未达到Wmax offs = ca->bic_K - t; else offs = t - ca->bic_K;//已经超过Wmax /* c/rtt * (t-K)^3 */ //计算立方,delta =| W(t) - W(bic_K) | delta = (cube_rtt_scale * offs * offs * offs) >> (10+3*BICTCP_HZ); //t为预测时间,bic_K为新Wmax所对应的时间, //bic_target为cwnd预测值,bic_origin_point为当前Wmax饱和点 if (t < ca->bic_K) /* below origin*/ bic_target = ca->bic_origin_point - delta; else /* above origin*/ bic_target = ca->bic_origin_point + delta; /* cubic function - calc bictcp_cnt*/ if (bic_target > cwnd) {// 相差越多,增加越快,这就是函数形状由来 ca->cnt = cwnd / (bic_target - cwnd);// } else {//目前cwnd已经超出预期了,应该降速 ca->cnt = 100 * cwnd; /* very small increment*/ } /* TCP Friendly —若是bic比RENO慢,则提高cwnd增加速度,即减少cnt * 以上次丢包之后的时间t算起,每次RTT增加 3B / ( 2 - B),那么能够获得 * 采用RENO算法的cwnd。 * cwnd (RENO) = cwnd + 3B / (2 - B) * ack_cnt / cwnd * B为乘性减小因子,在此算法中为0.3 */ if (tcp_friendliness) { u32 scale = beta_scale; delta = (cwnd * scale) >> 3; //delta表明多少ACK可以使tcp_cwnd++ while (ca->ack_cnt > delta) { /* update tcp cwnd */ ca->ack_cnt -= delta; ca->tcp_cwnd++; } if (ca->tcp_cwnd > cwnd){ /* if bic is slower than tcp */ delta = ca->tcp_cwnd - cwnd; max_cnt = cwnd / delta; if (ca->cnt > max_cnt) ca->cnt = max_cnt; } } ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack; if (ca->cnt == 0) /* cannot be zero */ ca->cnt = 1; //此时表明cwnd远小于bic_target,增加速度最大 }
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.153.3152&rep=rep1&type=pdf
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.37.7442&rep=rep1&type=pdf