0
点赞
收藏
分享

微信扫一扫

TCP吞吐性能缺陷的根源

皮皮球场 2022-01-08 阅读 28

TCP如何优化吞吐率,我很直接说优化不了,这让我甩开了很多令人尴尬的麻烦事儿。但我可以聊一聊。

为什么优化不了?因为滑动窗口。

为什么因为滑动窗口?因为滑动窗口本质上是一个“停-等”策略。

“停-等”怎么了?因为要等,所以要停,一停就慢了(也叫HoL阻塞)。

等什么?等buffer被填满。

什么buffer?就是接收窗口。

等接收窗口被填满有什么问题吗?问题就在这里!

那么要取消滑动窗口吗?这个最后再谈。先说本质问题。

问题在于,这块作为滑动窗口的buffer有一个约束,要求其中的字节序列号必须连续。这意味着这个buffer便不再是一块无差别随机访问的内存了,而变成了一个队列。一旦出现一个hole,传输过程就必须停下来等待它被填充,期间窗口的滑动是被阻滞。

诞生于1970~1980年代的TCP从来就不是为性能而生的。当人们意识到性能问题的时候,随即就出现了依赖out-of-order queue的selective ACK。但于本质问题无补,基因决定了上限。

设计传输协议,肯定要把端到端语义和传输语义分开,不然还要什么分层模型。简单说,传输只关注数据包看不见序列号,端到端只重排字节看不见数据包。这需要在字节流的序列号空间和数据包之间做一次映射。

问题在于,致使TCP没有作出这种设计的根源是什么?

在于它的“流式设计”。下面的链接可以找到故事的一部分:
https://historyofcomputercommunications.info/section/8.4/transmission-control-protocol-(tcp)-1973-1976/
分歧在于拆包和组装,为了让分组交换站在电路交换的对面,重新组装由端到端负责,这就不强求TCP流经过相同路由器了,但这个策略未竟全功。

未竟全功,核心原因是TCP协议被设计的时候并没有承载TCP数据的“数据报协议分组”,这是一个先有鸡先有蛋的问题,在1973年~1976年,IP尚未从TCP中分离出来,所以IP无法承载TCP,更别提UDP了。UDP是当IP从TCP中分离出来后,作为IP协议的等价语义在TCP层的补充而被后来添加的。

那么TCP只能自己承载自己,直接在分组交换网传输。

允许乱序是分组交换的基本特征之一,TCP协议也因此将保序逻辑放在了端到端,然而对于传输逻辑,TCP依然更像一条虚电路流,而不像数据报分组。

这是TCP后续停-等buffer,GBN低效的根源。为什么呢?

整个传输逻辑的实体不是TCP数据包,而是没有边界的TCP字节流。换句话说,发送端一次发送1000字节的数据,到了接收端,可能收到2个数据包,一个20字节,另一个980字节,也可能收到1000个数据包,每一个1字节,当然,更大可能是依然是一个1000字节的数据包。无论如何,接收端无法“基于数据包做ACK,只能对字节做ACK”,但主机处理处理依然以数据包为单位。这逆转了人们设计协议时对时间和空间做trade-off时的偏好。

对空间的关注超过了对吞吐的关注。为每一个字节安排一个ACK,将使TCP协议头变得冗长到不可接受,这不仅不能带来吞吐的提升,还会消耗大量的带宽用于ACK本身的管理开销。

摒弃数据包的概念,直接对TCP的字节流对积累ACK可以解决上述问题。积累ACK自然而然就等价于滑动窗口和GBN了。

整个故事是,流式抽象导致了必须基于字节进行ACK,进而积累确认和GBN重传被认为是性价比最高的。这是一个很低效的设计,但管用。

selective ACK是一种向正确方式的回归,但依然没能摆脱“基于字节ACK”的梦魇,SACK看上去很别扭并勉强,因为它不得不委曲于TCP Option中,空间受限而不能求全,本质原因还是最初的那个,如果给SACK安排了不受限制的空间,ACK报文将变得冗长。

我简单画了一个TCP本应该在分组交换网中的正确姿势:
在这里插入图片描述

非常清晰的两层结构。接收窗口限制了可用buffer的大小,而buffer只有够不够的问题,如果空间不足,显式发送抑制消息给ACK逻辑即可,ACK将抑制消息反馈到源端。

与buffer的解释类似,网络传输只有带宽满不满,拥塞与否的问题,而这些完全通过congestion control,pacing逻辑等完成,令牌桶里完全序列号的概念。

端到端语义和传输语义分离的好处不胜枚举:

  • 很容易统计重传率指标,需要传输的字节在端到端子层,实际传输的字节在传输子层。
  • 网络传输可以肆意并行,无需关注乱序,重组等问题,以最大化带宽利用率为目标。
  • 确认数据包而不是确认字节,灵活实现积累ACK或selective ACK,ACK报文可压缩,可编码。

简单展示一个ACK报文的样子:
在这里插入图片描述

现在看在新的设计下,滑动窗口本身的问题。要不要取消它?滑动窗口要取消,问题在于,首先,如何做流量控制,其次,流量控制如何使用buffer。

第一个问题很容易:

  • buffer是标量而非矢量,只看大小不看方向,只要尚未超过最大使用限额,就可以收数据。
  • 设置可用buffer最大值以及最大允许的突发,超过限额即发送源抑制。

看第二个问题。如果buffer中数据有hole不连续,应用程序如何收走数据?如果数据一直不被收走,buffer总会没有空闲空间。这不是又回到问题的原点了吗?问题的解决在于使用buffer的方式。

不同于标准TCP一个连接独占buffer的方式:
在这里插入图片描述

新的设计采用子连接复用的方式让多个连接共享同一个buffer:
在这里插入图片描述

buffer粒度更细,施展空间更大,同一子连接HoL阻塞概率更低。

QUIC大概就是这个意思吧。

我不认同NAK的原因还是因为它会引入纷乱的误判。

现在,可以把上面的设计部署在IDC网络使用流体渗透原理去对抗incast,也可以部署在MPTCP逻辑通道上。

浙江温州皮鞋湿,下雨进水不会胖。

举报

相关推荐

0 条评论