高吞吐,大并发,不怕数据大,最怕数量大。
网络处理怕小包,文件操作怕小文件,无论每一个小包还是每一个小文件,它们均消耗元操作和元数据,如每一个数据包都触发一次中断,而中断是有固有调度开销的,中断数量越多,开销越大。小文件类似,再小的文件也要有inode,ident等固定元数据描述。
元开销是固定的。
时间方面和空间方面元开销都与数量正相关。时间开销体现在并发调度,本质上是查找,这个已经说了好多次。空间上是元数据开销,这个不谈。
时间开销优化方法两种:
第一点最好的例子就是轮询替换中断,着重说第二点。
类比码头货物集散,为每一个包裹分配一辆卡车,就是小包场景,大量车辆将堵死道路,且需要大量的司机及装卸装置。因此采用集装箱才是提高吞吐的好方法,一辆卡车运输尽可能多的货物,以集装箱为单位装卸。
所谓机械磁盘调度算法也是希望磁盘转一圈尽可能读写更多数据。说到底还是希望减少操作数量,减少与数量正相关的损耗而提高吞吐。
当数据经过TCP或UDP隧道的时候,提高吞吐的最好方法就是将多个小包聚合成一个大包,类似在物理网卡执行一次GRO操作。
以wireguard-go为例,将1420 MTU大小的包聚集成65400字节的大包后,25Gbps网卡直连隧道,单流吞吐从不到1Gbps提升到了10Gbps。这里很容易理解省去了哪些开销:
- 批量加解密,数据量越大,预处理次数越少。
- tun设备,读写buffer越大,copy次数越少。
- channel操作,包越大,包量少,竞争越少。
- socket读写,buffer越大,读写竞争越少。
- socket读写,buffer越大,copy次数越少。
- …
减少数量,降低的就是元开销。
通过批量操作将数量降下来后,分离双工就是最后一击了。前面提到TCP的半双工实现,socket读写会互斥,为每一个peer创建两条隧道,分管两个方向,预期吞吐又会提升,实际10Gbps终于到了14Gbps。
除了减少数量,分离方向,其它的都是把戏。
只有抓住了核心瓶颈才能做优化,否则注定一场空。清晨有感而发。
浙江温州皮鞋湿,下雨进水不会胖。
|