直接原因是 window size 不够大。然而根本原因其实并非 delay ,“时分复用”系统的类比也不大对,应该是 TCP congestion control 导致的。
理想情况下, window size 足够大,在等待客户端回 ACK 的 delay 期间,服务器端的 window 还没有用完,就可以继续发送数据,从而不会出现任何形式的中断暂停, pipeline 的优势得到了最大程度的发挥。在这种情况下, delay 多大其实没关系——只要数据大小足够大,一开头的一个 delay 对整个传输时间没什么影响。也就是说最终还是瓶颈带宽(bps)起决定性作用。
然而实际情况并非如此。一个网络中并不仅仅有你这么一个连接,单说两个端点处都肯定是如此,更不用说中间大家共用的网络线路了。这种情况下, TCP 的 congestion control 机制就会起作用,通过调节 window size 来避免出现拥塞,因为一旦出现阻塞丢包对整个网络和自身都是很不好的。一般来说最终 window size 无法达到上面说的“理想值”,从而使得传输需要停下来等 ACK ,带宽也就不能被“充分利用”。
注意到 congestion control 的算法是 per connection 的,而不是 per server-client pair 。不管实际细节如何(什么 AIMD 、 slow start 等等),最终算法的目的是 connection-wise fairness ——在不出现拥塞的前提下,让每个 connection 都能大致得到同样的、最大的带宽。所以,如果你开多个连接,你就能”抢占“到更多的带宽……
补充一些细节(感谢
),多个 TCP 连接还能减少、摊平单连接时 congestion control 导致的 window size 大幅波动对速度的影响。早期 TCP 在遇到拥塞(丢包或者 duplicate ACK )时,会大幅减小 window size (甚至直接减到 1 MSS ),而增加又是缓慢的线性递增; TCP Reno 中改进为若是 triple duplicate ACK ,就会把 window size 减半而不是一减到底,增加时也会有一个在小于 ssthreshold 时使用翻倍增加而非线性递增的 slow start 过程,但即使如此波动还是很大(使用这类 AIMD 控制是为了保证“正确性”,即最终的公平分配,
Additive increase/multiplicative decrease)。还有一些其他细节可以参看他答案中的那篇文章。
----------------
理解了上面这个你也就能明白,为什么早年大家都用单线程下载的时候,你用一个网络蚂蚁、 FlashGet 就能快得飞起,而现在大家都用迅雷(甚至浏览器也都开始默认多线程下载了),你再也不能比别人快出那么多了……至于再开更多的线程,实际上意义不大。考虑到各种 overhead ,目前的因特网中,一对节点之间开个10线程就差不多了,到百级别就得不偿失了。
相关标签: