当前位置: 首页 > article >正文

多路径 TCP 调度的另一面

参考前面的文章 一个原教旨的多路径 TCP 和 MP-BBR 公平性推演,一直都破而不立,不能光说怎样不好,还得说说现状情况下,该如何是好。

如果 receiver 乱序重排的能力有限(拜 TCP 所赐),如果非要在多路径上传输 TCP,如果不考虑公平性,单单如何提高吞吐确实难,要找出来原因, 才能彻底解决它,得先分析问题。

设一条 TCP 连接存在两条子路径(subflow):

  • 快路径(Path1):带宽 B1,单向时延 D1;
  • 慢路径(Path2):带宽 B2,单向时延 D2,且 B1 ≥ B2, D1 ≤ D2。

发送策略:

  • 固定间隔 Round-Robin:每间隔 T 时间,轮流在 Path1 和 Path2 上各发送 1 个包;
  • 单快路径(Path1 Only):所有数据仅通过 Path1 发送,间隔 T 时间发送 1 个包。

假设:

  • 所有数据包大小均为 L 字节;
  • 接收端需按序重组数据,乱序包会导致阻塞。

分析 Round-Robin 吞吐量:

  • 发送速率
    • Round-Robin 策略下:
      • 每 2T 时间发送 2 个包,Path1 和 Path2 各 1 个;
      • 平均发送速率: 2 L 2 T = L T \dfrac{2L}{2T}=\dfrac{L}{T} 2T2L=TL
    • 路径带宽限制
      • Path1 的带宽 B1 满足: L T ≤ B 1 \dfrac{L}{T}\le B_1 TLB1
      • Path2 的带宽 B2 需满足: L T ≤ B 2 \dfrac{L}{T}\le B_2 TLB2
      • 由于 B1 ≥ B2,若 L T > B 2 \dfrac{L}{T}\gt B_2 TL>B2,Path2 会丢包,因此最大稳定发送速率需满足: L T ≤ B 2 \dfrac{L}{T}\le B_2 TLB2 T ≥ L B 2 T\ge\dfrac{L}{B_2} TB2L,即 T 不能太小,否则慢路径无法及时传输。
  • 接收端乱序阻塞
    • 数据包到达时间
      • Path1 的包到达时间:D1 + k⋅2T,k 为轮次;
      • Path2 的包到达时间:D2+k⋅2T
    • 乱序条件
      • 若 D2 + k⋅2T > D1 + (k + 1)⋅2T,即 D2 − D1 > 2T,则 Path2 会阻塞 Path1,快路径要等慢路径。
    • 有效吞吐量
      • 由于乱序阻塞,系统吞吐量受限于慢路径的到达速率,慢路径形成漏桶短板;
      • 每 2T 时间可交付 2 个包,但实际受 D2 限制,因此 G o o d p u t R R = 2 L 2 T + m a x ( 0 , D 2 − D 1 − 2 T ) \mathrm{Goodput}_{RR}=\dfrac{2L}{2T+\mathrm{max}(0,D_2-D_1-2T)} GoodputRR=2T+max(0,D2D12T)2L,若 D2−D1>2T,则吞吐量进一步下降。

再分析单快路径的吞吐量:

  • 发送速率:
    • 每 T 时间发送 1 个包,速率 L T ≤ B 1 \dfrac{L}{T}\le B_1 TLB1
  • 无乱序问题,所有包按顺序到达,接收端无需等待。
  • 有效吞吐量: G o o d p u t S i n g l e = L T \mathrm{Goodput}_{Single}=\dfrac{L}{T} GoodputSingle=TL

效率对比的结论非常明确, G o o d p u t S i n g l e ≥ G o o d p u t R R \mathrm{Goodput}_{Single}\ge\mathrm{Goodput_{RR}} GoodputSingleGoodputRR,当且仅当 Path1 与 Path2 效率相同时取等号,两条路径差异越大,吞吐下降越明显,这就是拖后腿。

力气小的经理和力气大的工人一起扛砖头,不如让力气大的自己去扛,两人的协调性成本太大,正与 TCP 乱序重组协调性成本太大一致。怎么办?好办!

让力气小的经理自己去搬他自己的砖,不要和力气大的工人协调。换到 TCP,还是老方法论,横竖一颠倒,转换一个视角,将空洞留在 sender 而不是 receiver。

怕在 receiver 处快路径等慢路径,那就慢路径笨鸟先飞,先到了等快路径即可,落实到具体操作也简单:

  • 在 sender 处,快路径从发送队列头部正序发送,慢路径从发送队列尾部反序发送(最后我再解释为什么),它们处理各自的丢包重传,慢路径直到和快路径 gap 小于阈值 K = f ( R T T ) K=f(\mathrm{RTT}) K=f(RTT)(亦可为一个经验值常量) 后停止发送,推动下一个窗口。

在这里插入图片描述

这很好解释,协调性不一致会付出成本时,解除协调性耦合即可,耦合就会导致木桶效应,各干各的更合适。

直观上理解,如果我们有 4GB 的文件,一快一慢两条路径,快路径依次发送 0,1,2,3,…,慢路径依次发送 4G - 1,4G - 2,4G - 3,…,假设它们在 X 字节处相遇,设快路径平均吞吐为 B,总传输时间将会从 4 G B B \dfrac{4GB}{B} B4GB下降到 X B \dfrac{X}{B} BX

至于 receiver 缓冲区问题,那不是问题,由于已经完全解耦了两个路径的传输,也就成了两个独立的流,各自维护接收缓冲区,最后将它们拼接在一起也行。

另一个问题,看起来以上策略仅对下载大块数据有效,对强调时延稳定性的业务不好使,其实也未必,问题的关键在于业务能容忍多大的 buffer 时延,只要该 buffer 大于多路径最大 BDP,就能应用此方案,只是把上面的 4GB 换成了该 BDP + a。

总之,总要拿个什么来交换成本,以获得收益,越复杂越不划算。举个例子,对于获得肉类的需求而言,驯养食肉动物的转化率远没有食草动物高,因为食肉动物处在食物链更上的生态位,它更复杂,也更不划算,复杂性本身就是成本的组分。

Linux Kernel 自带的 MPTCP 调度算法跟我这个看起来类似,但它没有反着发,而是再慢路径直接发送 s e q = S E Q c u r r + α ⋅ B D P f , 其中 α = R T T s R T T f \mathrm{seq}=\mathrm{SEQ_{curr}}+\alpha\cdot\mathrm{BDP}_f,其中 \alpha=\dfrac{\mathrm{RTT}_{s}}{\mathrm{RTT}_{f}} seq=SEQcurr+αBDPf,其中α=RTTfRTTs,思路依然是让慢路径序列号往后错,但它仍倾向于精确拼接,企图两边正好配合,但这是不可能的,慢路径非常容易与快路径耦合,一旦被快路径赶上,Seq 在快慢路径交错传输和重传,就会引发持续性 HoL 抖动,而这个错开量 α ⋅ B D P f \alpha\cdot\mathrm{BDP}_f αBDPf 由于 RTT 和吞吐率本身的测量误差几乎不可能太准确,也就难怪效果不好了。

之所以倒着发,为了保证慢路径后段一定与快路径解耦(确保后边的数据已经完全准备好),即使 gap 算大了,仅靠快路径自身也能很快接上慢路径已经准备好的数据,而如果 gap 算小了,则影响更小,换句话说,即使抖动也是一瞬间,且快路径主导补洞。

当然,TCP 本身并不支持倒序发送,那这个就不是 TCP 了。既然不是 TCP,索性就多做一点,连 RTT 测量问题也给解了。学学 Swift,到处打标时间戳就很高尚。

其实要更精确地测量两条路径的 RTT 差异非常容易,无论哪条路径收到一份数据需要确认时,将确认报文同时发到多条路径上,sender 只需要比较两个确认报文的时间差,并乘以快路径的吞吐即可获得偏移量,两个问题遗留:

  • 这可能只是反向单向时间戳之差,而正向时间戳更需要,让 receiver 采集数据包时间戳信息并回送回来;
  • 确认报文双发会不好浪费带宽,会,但并不严重,且这是小包加速的典型套路。

既然重新做协议,不要尽抄 TCP 就是了,但要尽量保持简单。

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

相关文章:

多路径 TCP 调度的另一面

参考前面的文章 一个原教旨的多路径 TCP 和 MP-BBR 公平性推演,一直都破而不立,不能光说怎样不好,还得说说现状情况下,该如何是好。 如果 receiver 乱序重排的能力有限(拜 TCP 所赐),如果非要在多路径上传输 TCP&…...

vcpkg安装指定版本的库

一.vcpkg安装 使用git将vcpkg源码克隆到本地制定目录(D:\vcpkg),并初始化 git clone https://github.com/microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh # Linux/macOS .\bootstrap-vcpkg.bat # Windows 如下图: 二.安…...

【工具变量】上市公司供应链稳定性数据两个维度(2013-2023年)

供应链稳定性是指供应链在面对各种内外部因素的冲击和不确定性时,能够保持持续、顺畅运作的能力,而供应链稳定性指数是用于评估企业在其供应链管理中保持稳定性的一个重要指标。本分享数据参考钟涛(2022)、董浩和闫晴(…...

Redis场景问题2:缓存击穿

Redis 缓存击穿是指在缓存系统中,大量请求(高并发访问)同时访问一个不存在于缓存中(一般是因为缓存过期或者数据未被加载到缓存)但在数据库中存在的热点数据,从而导致这些请求直接穿透缓存层,涌…...

RocketMQ - 从消息可靠传输谈高可用

先稍微介绍下RocketMQ架构。 主从架构 Broker 集群:每个 Broker 分为 Master 和 Slave 角色,Master 负责读写,Slave 作为热备。 同步复制(SYNC_MASTER):消息写入 Master 后,需等待 Slave 同步完…...

ES拼音分词自动补全实现

#测试拼音分词 POST /_analyze { "text":"如家酒店真不错", "analyzer": "pinyin" } #这里把拼音的首字母放到这里,也说明了这句话没有被分词,而是作为一个整体出现的 #还把每一个字都形成了一个拼音&#…...

golang 日志log与logrus

目录 一、Go 标准库 log 详解 1. 功能特点 2. 常用函数 3. 示例代码 4. 优势和局限 二、第三方库 logrus 详解 1. 功能特点 2. 核心功能 3. 示例代码 4. 优势和扩展性 三、总结 1. 何时选择 log? 2. 何时选择 logrus? 3. 对比总结 一、Go 标…...

EFISH-SBC-RK3576 + 5G模组:无线工业相机与分布式AI质检‌

在智能制造与仓储物流场景中,传统有线工业相机存在部署成本高、灵活性差等痛点。‌eFish-SBC-RK3576‌ 通过 ‌5G无线传输 分布式NPU协同‌,实现跨产线、跨工厂的AI质检系统,检测效率提升300%,布线复杂度降低90%。 ‌1. 系统架构…...

C/C++ 基础 - 回调函数

目录 前言 回调函数预备知识 函数指针 什么是函数指针 函数指针的语法 如何用函数指针调用函数 函数指针作为函数的参数 函数指针作为函数返回类型 函数指针数组 回调函数 什么是回调函数 为什么要用回调函数 怎么使用回调函数 总结 前言 在写项目的时候&#x…...

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的消息队列:使用 RabbitMQ 实现异步处

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、开篇整…...

高项第十六章——项目采购管理

什么是采购管理&#xff1f;项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程。 项目采购管理包括编制和管理协议所需的管理和控制过程。 16_1 管理基础 什么是协议&#xff1f;协议是用于明确项目初步意向的任何文件或沟通结果&#xff0c;协议的范…...

宽带的带宽

宽带的带宽是指在一定时间内通过网络连接传输数据的能力&#xff0c;通常用**比特率&#xff08;bit/s&#xff0c;即每秒传输的比特数&#xff09;**来表示&#xff0c;比如100Mbps&#xff08;兆比特每秒&#xff09;。带宽越大&#xff0c;理论上网络传输数据的速度越快&…...

DeepSeek分析仿写选题应该怎么做?

目录 选题分析&#xff1a;AIGC在学术写作中的应用及其与作者背景的关系 1. 选题背景与意义 2. 研究问题 3. 研究方法 4. 主要发现 5. 研究贡献 6. 研究局限与未来方向 7. 结论 8. 未来研究方向 大家好这里是AIWritePaper官方账号&#xff0c;官网&#x1f449;AIWrit…...

19840 Dijkstra求最短路2

19840 Dijkstra求最短路2 相较于1&#xff0c;数据增强了&#xff0c;要用堆来优化&#xff0c;也就是优先队列。 ⭐️难度&#xff1a;中等 &#x1f31f;考点&#xff1a;Dijkstra、最短路问题 &#x1f4d6; &#x1f4da; import java.util.*;public class Main {static…...

docker-compose部署prometheus+grafana+node_exporter

目录 docker-compose文件 配置文件 文件层级关系&#xff0c;docker-compose和配置文件位于同级目录 node_exporter页面json文件 涉及离线包 一.docker-compose文件 [rootsulibao prometheus]# cat docker-compose.yml version: 3services:prometheus:image: registry.c…...

Oracle数据库数据编程SQL<递归函数详解>

递归函数是一种在函数体内直接或间接调用自身的函数。这种函数通过将复杂问题分解为更小的相同问题来解决特定类型的编程任务。 目录 一、递归函数基本概念 1. 递归定义 2. 递归工作原理 二、递归函数示例 1. 经典阶乘函数 2. 斐波那契数列 3. 计算数字位数 三、递归查…...

Redis-08.Redis常用命令-有序集合操作命令

一.有序集合操作命令 ZADD key score 1 member1 [score2 member2]&#xff1a; zadd zset 10.0 a 10.5 b ZRANGE key start stop [WITHSCORES]: zrange zset 0 -1 为何顺序为a&#xff0c;c&#xff0c;b&#xff1f; 因为 zrange zset 0 -1 withscores zrange key start …...

LLaMA-Factory使用实战

LLaMA-Factory使用实战 项目介绍 项目地址&#xff1a;https://github.com/hiyouga/LLaMA-Factory 中文文档&#xff1a;安装 - LLaMA Factory 快速开始文档&#xff1a;https://zhuanlan.zhihu.com/p/695287607&#xff08;推荐参考&#xff09; 远程服务器通过本地代理加…...

读一本书,骑行万里路:与维乐 Angel Rise+骑行看世界

最近读到了一本名为《自行车改变的世界&#xff1a;女性骑行的历史》的书&#xff0c;才发现原来女性的骑行自由来得并不轻易&#xff0c;激励着每一位女性勇敢地踏上骑行之路。而我一直在使用的维乐坐垫品牌&#xff0c;除了产品专业之外&#xff0c;也一直都非常关注女性骑行…...

【大模型】SpringBoot整合LangChain4j实现RAG检索实战详解

目录 一、前言 二、LangChain4j 介绍 2.1 什么是LangChain4j 2.2 LangChain4j 主要特点 2.3 Langchain4j 核心组件 三、RAG介绍 3.1 什么是RAG 3.2 RAG工作流程 3.2.1 补充说明 3.3 Embedding模型 3.3.1 RAG实际使用步骤 3.3.2 什么是Embedding 3.3.3 Embedding 技…...

流动的梦境:GPT-4o 的自回归图像生成深度解析

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

北大人工智能研究院朱松纯:“中国的AI叙事” 存在认知偏差

3月29日&#xff0c;在2025中关村论坛通用人工智能论坛上&#xff0c;北京通用人工智能学院院长&#xff0c;北京大学人工智能研究院、智能学院院长朱松纯表示&#xff0c;目前&#xff0c;行业对AI的讨论几乎被大模型能力所占据&#xff0c;而基础学科、原始创新与智能本质的研…...

习题1.26

解释题&#xff0c;说简单也简单&#xff0c;难在如何表达清楚。 首先解释下代码的变化 (defn expmod[base exp m](cond ( exp 0) 1(even? exp) (mod (square (expmod base (/ exp 2) m)) m):else (mod (* base (expmod base (- exp 1) m)) m)))(defn expmod[base exp m](co…...

FPGA调试笔记

XILINX SSTL属性电平报错 错误如下&#xff1a; [DRC BIVRU-1] Bank IO standard Vref utilization: Bank 33 contains ports that use a reference voltage. In order to use such standards in a bank that is not configured to use INTERNAL_VREF, the banks VREF pin mu…...

基于Java(SSM)+Mysql实现移动大厅业务办理(增删改查)

基于 SSM 框架的移动业务大厅 数据库需要自行创建&#xff01; 一、 整体基本实现情况 对本学期的 Java 作业 1 的 SOSO 移动大厅进行改进&#xff0c; 基于 SSM、JSP、Maven、Tomcat、MySQL 等实现。 二、 实现详情 1、 工程结构图 2、 工程结构各部分实现 &#xff08;…...

音视频 ColorSpace色彩空间详解

前言 基于前篇介绍YUV格式,本文继续介绍另一个重要概念颜色空间,又叫色彩空间;主要用于在音视频开发中的色彩空间转换。 色彩空间Color Space 色彩空间由色彩模型和色域共同定义。当色彩模型与特定的描述相关联以后,就称为色彩空间。 色彩模型Color Model 色彩模型Col…...

【字符设备驱动开发–IMX6ULL】(一)简介

【字符设备驱动开发–IMX6ULL】&#xff08;一&#xff09;简介 一、Linux驱动与裸机开发区别 1.裸机驱动开发回顾 ​ 1、底层&#xff0c;跟寄存器打交道&#xff0c;有些MCU提供了库。 spi.c&#xff1a;主机驱动&#xff08;换成任何一个设备之后只需要调用此文件里面的…...

45 55跳跃游戏解题记录

先是55跳跃游戏&#xff0c;暴力解法会怎样&#xff1f;会超出时间限制&#xff0c;而且有很多细节要注意&#xff1a; func canJump(nums []int) bool {// 处理空数组情况&#xff0c;当nums只剩一个元素时&#xff0c;nums[i:]导致越界。if len(nums) 0 {return false}// 如…...

C++_STL之list篇

一、list的介绍 std::list是C标准模板库(STL)中的一个双向链表容器。与vector和deque不同&#xff0c;list不支持随机访问&#xff0c;但它在任何位置插入和删除元素都非常高效。 1.基本特性 (1)双向链表结构&#xff1a;每个元素都包含指向前驱和后继的指针 (2)非连续存储&…...

Go中的逃逸分析

什么是逃逸&#xff1f; 逃逸是指一个变量本来应该分配在栈&#xff08;stack&#xff09;上&#xff0c;但由于某些原因&#xff0c;最终被分配到了堆&#xff08;heap&#xff09;上。 类比&#xff1a; 栈就像一个临时的快餐盒&#xff0c;用来存放短期使用的数据。堆就像…...