“发送的数据总量相同,完成时间就应接近”忽略了到达过程。固定出口每毫秒服务一个报文时,24 个报文均匀到达可以几乎不排队;同样 24 个报文在很短窗口内到达,最后一部分会等待多个服务周期。

本篇把这个判断做成一个小型可复现实验。研究问题是:总报文数、报文大小和服务率不变时,发送同步程度是否改变队列等待分布;pacing 能否在该模型内缩短尾部等待。第 19、20、35 篇提供拥塞、队列和 incast 前置知识。

假设、基线与判定标准

实验使用离散 FIFO 单服务台。每个报文的服务时间固定为 1000 微秒,每次运行处理 24 个等长报文。三种到达模式只改变时间分布:

  • steady:单个来源每 1000 微秒提交一个报文,并加入 0–200 微秒固定种子抖动。
  • burst:24 个报文都在 0–200 微秒窗口到达。
  • paced:三批各有 8 个报文,批起点相隔 8000 微秒,每个报文再加 0–200 微秒的固定种子抖动。

种子固定为 7、19、43。每次运行保存每个报文的到达时间、服务开始/结束时间、等待值和到达时前方未完成报文数(包含正在服务的报文),并计算平均值、p50、p99 与最大值。本次研究假设为每个种子都满足 burst p99 > paced p99 > steady p99。脚本总是先输出数据,再报告假设在各次运行中是否得到支持;改参数后即使排名变化,也不会因预期排名断言而在输出前终止。

这里的 p99 使用 nearest-rank:对 24 个等待值排序,取第 ceil(0.99 × 24) 个。样本很小,因此 p99 实际等于最大值;文章不会把它外推成线上 p99。

模式提炼:一次只改变待研究变量

固定服务模型与工作量 + 改变到达过程 + 重复固定种子 + 保存原始值

性能结论需要同时交代输入、统计口径和反例。只给一张平均值图,无法判断尾部是否由少数突发产生。

可复跑实现

queue_experiment.py 使用 Python 标准库生成到达时间,按 start = max(arrival, previous_finish) 计算 FIFO 服务开始时刻,等待时间为 start - arrival,服务结束为 start + service_time。“模型实现正确性”只检查这三条算术关系;“研究假设是否成立”另行比较三种模式的 p99。前者通过不会使后者自动成立。

unfinished_packets_ahead_at_arrival 统计到达时完成时间严格大于到达时间的前序报文,包含正在服务的报文,不等同于前序等待报文数。到达 [0, 500]、服务时间 1000 时,第二包前方未完成报文数为 1、前序等待报文数为 0、等待时间为 500 微秒。前序报文恰在到达时完成则不计入;脚本在每次输出前断言这两个边界。

1
2
python3 source/_posts/2026-09-24-计算机网络39-独立研究小项目/queue_experiment.py --help
python3 source/_posts/2026-09-24-计算机网络39-独立研究小项目/queue_experiment.py

素材包括实验配置、实验脚本、逐报文原始数据与汇总、资料与运行记录和审阅记录。

结果与解释

实际输出如下,单位均为微秒:

seed 模式 mean p50 p99 max
7 steady 82.333 59 157 157
7 paced 3439.042 2974 6871 6871
7 burst 11435.042 10948 22843 22843
19 steady 103.417 117 173 173
19 paced 3427.333 2953 6875 6875
19 burst 11416.667 10928 22811 22811
43 steady 64.083 55 191 191
43 paced 3420.292 2984 6902 6902
43 burst 11386.625 10886 22809 22809

三个种子都支持本次排名假设。steady 的 p99 从 157 到 191,paced 从 6871 到 6902,burst 从 22809 到 22843。样本只有 24 个,nearest-rank p99 就是最大值,因此这里的 p99 不是稳定的尾延估计。

批间隔 8000 恰好等于 8 × 1000 微秒,不是大于一批的服务时间。每批内最早到达的报文带有随机相位,因此前一批的完成时刻可以跨过下一批的首个到达时刻。下图取原始时间线中的关键点:

1
2
3
4
5
6
7
8
9
seed 7 : batch 1  first=12    [service 12 ........ 8012]
batch 2 first=8009 ^ overlap=3 us
[service 8012 ........ 16012]
batch 3 first=16015 gap=3 us

seed 43: batch 1 first=9 [service 9 ......... 8009]
batch 2 first=8004 ^ overlap=5 us
[service 8009 ........ 16009]
batch 3 first=16095 gap=86 us

seed 19 的第二批在 8027 微秒到达,前一批在 8011 微秒结束,中间空闲 16 微秒;第三批则恰在前一批结束的 16027 微秒到达。pacing 在这个模型中缩小了单次突发规模,所以等待分布显著低于 24 包同批到达;它并未保证批间队列每次都完全清空。

与 RFC 8289 的对照

RFC 8289 §2 将 sojourn time 定义为报文进入缓冲区到离开缓冲区的时间(发送或丢弃均可导致离开)。§3.1 讨论估计器:对每个 RTT 都能消散的短突发,平均队列或平均驻留时间可能把短暂积压误判成持续队列;CoDel 因而关注近期时间窗内的最小 sojourn time。

该论点的前提包括:能在入队时记录时间戳,在离开缓冲区时计算 sojourn time,且观察窗口要长到足以覆盖预期的最大突发或相关 RTT。RFC 的评价方法是用近期最小驻留时间判断队列是否持续,而不是只看平均队列长度。

本模型只计算 FIFO 单服务台的到达 arrival_us、开始服务 service_start_us 和完成服务 service_end_us。wait_us = service_start_us - arrival_us 只表示服务前等待;服务开始不等于离开缓冲区,不能把 wait_us 直接称为 RFC 的 sojourn time。前方未完成报文数按完成时间计数,因此包含正在服务的报文。模型用逐包时间线和 p50/p99 比较三种到达过程,没有计算 CoDel 的时间窗最小值、TARGET、INTERVAL、drop state 和控制律,也没有 RTT、TCP/QUIC 反馈、多流竞争或真实链路。它只检查给定参数下的 FIFO 等待分布,不构成 CoDel 或真实网络性能验证。

模型没有反馈环,因此不能声称复现了 TCP 或 QUIC 拥塞控制。RFC 9002 中的拥塞窗口、ACK、丢包检测与 pacing 约束远比单服务台复杂。这里只验证一个有条件的排队结论:在给定的 FIFO 服务率下,相同总工作量的到达时序不同,会产生不同等待分布。

反例与证据强度

第一个反例是无限快出口。服务时间趋近零时,三种到达模式的等待都趋近零,同步本身不会造成可观测队列。

第二个反例是足够大的 pacing 间隔只改善发送端可控的突发。如果多个独立来源仍在同一时刻汇聚,单个来源 pacing 不保证消除 incast。

第三个边界是 FIFO。FQ-CoDel 把流隔离与受控延迟算法组合起来,CoDel 根据队列驻留时间采取动作。本文没有实现二者,不能从结果推导其真实参数或收益。

练习

练习一:把 pacing 间隔从 8000 微秒改为 4000 微秒,先预测 p99 如何变化,再运行脚本。解释哪一批报文开始与前一批队列重叠。

练习二:保持 24 个报文不变,把服务时间改为 100 微秒。说明为何 burst 的绝对等待下降,但“同步到达增加队列”的方向仍可能成立。

模式速查表

研究要素 本篇固定内容 结论边界
工作量 24 个等长报文 不代表真实业务大小分布
服务 FIFO、每包 1000 微秒 不代表交换机或内核调度
重复 固定种子 7、19、43 只覆盖 0–200 微秒抖动
原始数据 每包等待时间 没有真实时间戳与抓包
反例 快出口、短 pacing 间隔 结论必须带容量条件

官方一手参考资料

验证边界

已验证:Python 3 标准库在三个固定种子下运行 FIFO 离散模型,保存逐报文到达、服务开始/结束、等待和前方未完成报文数(包含正在服务的报文)。事件算术与研究假设分开报告;本次三个种子均支持排名假设,验证类型为 STATIC。

NOT_RUN:未使用真实 NIC、交换机、内核 qdisc、TCP/QUIC 实现、硬件时间戳或跨主机链路;没有真实吞吐和延迟测量。模型输出不得与论文或设备结果拼接成性能排名。