多线程下载一个大文件的速度更快的真正原因是什么?

多线程从网上下载一个大文件为什么要更快(比之单线程)?网上查了有人说“是因为io堵塞的原因,因为网速肯定快不过cpu,单线程单io通道,而多线程多io…
关注者
672
被浏览
255,207

43 个回答

这要根据网络实际情况分别讨论。胡乱归咎于滑动窗口是不负责任的。

根据我的经验,这个问题起码要分六个大类分别讨论——这六大类讨论完毕,还得再说说流控算法的公平性问题。


1、低延迟网络

低延迟网络指的是相对于网络最高带宽,信息传输延迟很低、不至于影响数据最高传输率的网络。

一般来说,这类网络无需考虑滑窗算法问题。

这是因为,除非局域网内部之类点对点传输的场景,一旦经过了路由器之类网络设备,数据传输就一定会被途中每个设备的缓冲区影响;一旦触发流控就说明缓冲区已经爆了、主动丢包了;那么此时“滑窗大小主动减半然后线性增长”其实起了“等待缓冲区清空”的作用,并不会严重影响设备吞吐率。

而点对点传输场景呢,正常来说链路层都有个码率协商过程;一旦协商成功,那就是“只要你能放到线路上对方就一定能接的住”,不涉及缓冲区也不会丢包(除非线路稳定性不足)——此时TCP的流控基本不起作用,设备压根就是一直压着线路最高传输速率跑。

1.1 无限速

实际网络上,你往往需要和其他用户共用一段链路(共用路由、共用小区光纤等等);此时多线程下载主要和资源竞争相关。

无限速条件下,你要和其他用户竞争使用同一条链路。当你传输信息时,其他用户就不得不等待;其他用户传输时,你也不得不等待。

大多情况下,路由器或其他智能网络设备公平调度每一条链路;当你多线程下载时,你名下的链路数就会增多,于是你得到调度的几率就按照你开的链路数线性增加——比如十个用户十条链路全部跑满的话,每个用户可以占有线路容量的1/10;但如果你开了三条链路,那么你一个人就占用了线路容量的3/12。这样你的下载速度自然就高了很多。

但这种情况会引起恶性竞争,使得每个人都盲目的多开链路,很快耗尽网络设备资源。

1.2 有限速

实际网络上可能存在很多限速策略。不同的限速策略、限速位置也会影响多线程下载的种种表现。

比如,服务器可能针对每用户限速;那么你开一百条链路也无法增加下载速度。

但这个策略太过复杂,实际上很难做到;所以网络供应商往往会为每个链接限速。那么你开N个链接自然就得到了N倍下载速率——但这实际上使得服务器为你一个人用了N倍的缓冲区空间,这是极其招人恨的。

因此,现在大多下载站限制每IP连接数,一般不允许超过3个。多开连接甚至可能被ban掉ip。


类似的,路由器也可以针对每个用户限速(此时多开连接并不增加下载速率);当你同时看视频并下载大文件时,这两个链接也会相互挤占带宽(这种挤占可能发生在你的PC终端以及途中的每个节点上)——所以当你BT下载同时看视频/玩游戏就可能卡顿。


总之,大多情况下,多开连接的确能挤占到更多的线路带宽;这或许就是很多人盲目多开连接搞多线程下载的原因所在——也是很多答案盲目的把“多开连接提高下载速率”归结于“滑动窗口”这个基础算法的原因。

但是,多开连接未必真能多挤占带宽。事实上,因为多开连接会过快消耗服务器资源,互联网工程师们反而花了很大精力去限制用户“借助多开连接得利”。


2、高延迟网络

随着网速提高以及跨国通讯业务增加,很多网络的延迟已经足以影响链路数据传输速率上限了。

这是因为,TCP需要借助“滑窗”这个缓冲区里面的数据实现重传、从而保证可靠通讯。具体说就是发送端必须收到接收端的确认报文(ACK)后,才敢废弃缓冲区里相应的数据。

设滑动窗口最大值为W字节,报文往返时间(发送端发送数据到收到对端返回的ACK报文所需的时间)为RTT,则数据最大传输率就等于 W/RTT。

对高延迟网络,RTT数值很大;那么如果W不够大,链路最大传输率就不可能高。

2.1 旧TCP协议的窗口大小

“滑窗”是要占用内存的;由于历史原因,TCP协议认为16位表示滑窗大小就足够用了。于是最大滑窗大小就只有64K字节。

在新兴的千兆光纤网(因为传输速率太高哪怕延迟很低也必须很大的滑动窗口才能跑满带宽上限)以及跨海电缆(延迟太高)上,这个大小实在太小了。因此后来不得不修改了协议,允许最高两个G的滑动窗口。

如果你用了很陈旧的操作系统的话,滑窗大小可能就有64K上限限制。这种情况下每条链路允许的最大传输率是远远跑不满线路容量的;此时多开链路相当于变相增大滑动窗口,传输速率自然增加。

2.2 新TCP协议的滑动窗口大小

当你的设备/操作系统的TCP协议栈不太旧时,虽然因为RTT过大导致滑动窗口大小增加不够快,但往往也只需若干秒就能跑到线路允许速率上限。此时情况和“低延迟网络”差别不大。


3、网络抖动

长距离数据传输时,尤其如3G/4G这样的公用无线信道很容易受到各种干扰,使得网络延迟(RTT)抖动。

3.1 低抖动网络

低抖动网络可按情形1、2讨论,不再重复。

3.2 高抖动网络

高抖动网络会严重影响流控算法,使得TCP滑窗大小不能按照预期增长。

当年我搞互联网链路聚合时做过实验,实验证明RTT在100~300ms间抖动时,TCP滑窗会很快停止增长,使得链路传输速率上限极低。这种抖动甚至比少量的丢包更能影响传输率(报文乱序造成大量DUP ACK时,链路传输率也会小幅下降,但影响也没有RTT剧烈抖动大)。

这种网络上,多开连接相当于人为强制加大滑窗大小,可以有效提高大文件下载速度。


4、流控算法的公平性

参与网络通讯的用户往往不止一个。那么,当不同用户使用不同流控算法时,它们是如何竞争带宽的呢?

比如说,UDP是不管报文有无丢失,只要网卡有空它就一刻不停的报文轰炸;而TCP呢,绝大部分流控算法会在报文丢失后主动降低数据传输率。那么,在一个UDP/TCP共存、且开足马力下载的网络环境里,TCP发送速率就会越来越慢。

换句话说,TCP会主动退让,而UDP不会。


类似的,不同的流控算法也有“是更倾向于侵占还是更倾向于退让”的不同“个性”。

那么不断开新连接、重复“慢启动”流程(慢启动是从很小的滑窗启动,速率是每RTT倍增的,反而是流控协议里增长最迅猛侵略性最强的阶段,增长一点都不慢)也能稍微多挤占一些资源;但后果是复杂的协商过程降低了线路上有效信息所占比率(一个TCP/IP头是带几个字节还是1K字节,带宽利用率显然是截然不同的),反而进一步恶化了通讯环境。

不仅如此,现在很多设备支持QoS优先级设置;比如游戏/视频之类延迟敏感的报文的优先级就可能高于文件下载。这种网络环境里,下载速率受到的影响就更为复杂了。


总结一下就是:

“多开线程增加下载大文件速率”大致来自于三个原因(或它们的组合):一是绕过不够严密的限速措施;二是在和其他用户/应用的竞争中挤占更多带宽;三是在高延迟高抖动网络里变相的强制增加滑动窗口大小,从而绕过TCP流控协议本身的缺陷。


除此之外,正常网络环境里,滑窗算法表现良好;多开线程反而会导致磁盘来回寻道、降低存储子系统吞吐率。在网络传输率和磁盘传输率基本匹配的应用环境里反而会造成负面影响(但大多情况下,硬盘传输带宽远大于网络带宽、再加上磁盘缓冲区的存在,因此最终影响不明显)。

首先如果你真的在万兆网当中测试过的话,会发现只要配置得当,一个TCP连接也完全可以将一张万兆网卡的带宽占满了。所以“多线程下载一个大文件的速度更快”这句话本质上来说就是错误的。

那为什么真实场景下用多线程下载似乎会快一些呢?

  1. 延迟高的情况下,一个TCP连接要占满线路带宽,需要有足够大的TCP window,通常超过默认的TCP window上限,需要通过TCP window scale扩展来增加window大小。当年的操作系统对window scale的支持不太好,增加TCP连接数相当于变相增大window。
  2. 操作系统的TCP流控实现不太合理的时候,遇到丢包可能速度会掉得很快,多个连接可以缓解这个问题,至少看到速度比较平缓,不会一下掉一半
  3. 某些网站限制了单个TCP连接的速度,或者本身因为实现问题单个TCP连接就有性能上限(比如错误地使用了很小的buffer来做IO),多个连接也就可以增加性能。