TCP BIC 擁塞控制源碼分析

    1、對於TCP協議數據傳輸優化來說最大的難點就在於物理帶寬的估計,網絡的複雜性決定了擁塞控制算法作用的有限性和Linux內核支持的擁塞控制算法(reno、newreno、hybla、bic、cubic、westwood、vegas、yeah等)的多樣性,只有準確的帶寬估計才能夠充分的利用帶寬,所以當前的主要難點是帶寬估計。

    2、BIC 主要用來解決高速大延時網絡(High-speed networks with large delays)的帶寬估計問題。

    3、BIC算法其實是看透了帶寬探測、估計的過程,所以其使用了二分查找技術來準確、快速的估計帶寬。



BIC二分查找邏輯分析:

    如上圖,橫軸是last_max_cwnd,意義是上次丟包時預估的帶寬值,這個值將對下一輪的帶寬估計起着關鍵影響,縱座標是snd_cwnd,這幅圖描述的是BIC在擁塞避免階段的snd_cwnd調節。  

    帶寬逼近過程(snd_cwnd < last_max_cwnd):     

         1、dist = last_max_cwnd - snd_cwnd:

         2、dist < A,snd_cwnd快逼近帶寬值預估值了,snd_cwnd的探測速度要放慢,否則可能會出現丟包。

         3、A < dist < B,那麼此時應該將snd_cwnd探測速度調節到適中值。

         4、dist > B,snd_cwnd離last_max_cwnd距離很遠,需要加快它的探測速度。

 

    新帶寬探測過程(snd_cwnd > last_max_cwnd):

         1、dist = last_max_cwnd - snd_cwnd:

         2、如果 dist < C,那麼需要慢速探測;

         3、如果 C < dist < D,那麼中速探測;

         4、如果 dist > D,那麼應該快速探測。


/* BIC TCP Parameters */
struct bictcp {
	u32	cnt;		/* increase cwnd by 1 after ACKs */
	u32 	last_max_cwnd;	/* last maximum snd_cwnd */
	u32	loss_cwnd;	/* congestion window at last loss */
	u32	last_cwnd;	/* the last snd_cwnd */
	u32	last_time;	/* time when updated last_cwnd */
	u32	epoch_start;	/* beginning of an epoch */
#define ACK_RATIO_SHIFT	4
	u32	delayed_ack;	/* estimate the ratio of Packets/ACKs << 4 */
};

   u32cnt; /* increase cwnd by 1 after ACKs */

     用來記錄進入擁塞避免狀態後,需要接收到多少個ACK才能出發snd_cwnd + 1

   u32  last_max_cwnd;/* last maximum snd_cwnd */

      丟包的時候,BIC需要選取一個snd_cwnd值作爲二分查找的上限值,如果當前二分查找的帶寬值大於上一次丟包記錄的last_max_cwnd那麼last_max_cwnd更新爲當前的snd_cwnd,否則爲0.9 * snd_cwnd,關於這個問題其實很好理解,如果當前探測到的帶寬值比設定的二分閾值小,那麼說明當前帶寬比之前預計的帶寬要小,所以下次預測帶寬值要縮小,縮小爲原來的0.9倍。

   u32 loss_cwnd; /* congestion window at last loss */

     用來記錄丟包時的擁塞窗口值。

   u32 last_time;/* time when updated last_cwnd */

      用來記錄最近一次擁塞避免階段snd_cwnd調節的timestamp,如果多次進入時間差很小那麼只會調節一次,避免頻繁的調節。

   u32	epoch_start;	/* beginning of an epoch */

     用來記錄計入擁塞避免狀態的timestamp,這個變量在BIC擁塞控制算法具體實現中,沒有太大意義。

<span style="font-size:14px;">   </span><span style="font-size:18px;">u32	delayed_ack;	/* estimate the ratio of Packets/ACKs << 4 */</span>

   用來記錄延時ACK的個數,BIC考慮的延時ACK對帶寬估計的影響。

/*
 * Compute congestion window to use.
 */
static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
{
	if (ca->last_cwnd == cwnd &&
	    (s32)(tcp_time_stamp - ca->last_time) <= HZ / 32)
		return;

	ca->last_cwnd = cwnd;
	ca->last_time = tcp_time_stamp;

	if (ca->epoch_start == 0) /* record the beginning of an epoch */
		ca->epoch_start = tcp_time_stamp;

	/* start off normal */
	if (cwnd <= low_window) {
		ca->cnt = cwnd;
		return;
	}

	/* binary increase */
	if (cwnd < ca->last_max_cwnd) {
		__u32 	dist = (ca->last_max_cwnd - cwnd)
			/ BICTCP_B;

		if (dist > max_increment)
			/* linear increase */
			ca->cnt = cwnd / max_increment;
		else if (dist <= 1U)
			/* binary search increase */
			ca->cnt = (cwnd * smooth_part) / BICTCP_B;
		else
			/* binary search increase */
			ca->cnt = cwnd / dist;
	} else {
		/* slow start AMD linear increase */
		if (cwnd < ca->last_max_cwnd + BICTCP_B)
			/* slow start */
			ca->cnt = (cwnd * smooth_part) / BICTCP_B;
		else if (cwnd < ca->last_max_cwnd + max_increment*(BICTCP_B-1))
			/* slow start */
			ca->cnt = (cwnd * (BICTCP_B-1))
				/ (cwnd - ca->last_max_cwnd);
		else
			/* linear increase */
			ca->cnt = cwnd / max_increment;
	}

	/* if in slow start or link utilization is very low */
	if (ca->loss_cwnd == 0) {
		if (ca->cnt > 20) /* increase cwnd 5% per RTT */
			ca->cnt = 20;
	}

	ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack;
	if (ca->cnt == 0)			/* cannot be zero */
		ca->cnt = 1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章