6. 源码分析:TCP CUBIC 拥塞控制
网络源码很多,但不需要全读完。本节聚焦一个对 AI 推理和大数据传输都很关键的机制:Linux 内核中的 TCP CUBIC 拥塞控制算法。
6.1 为什么选择 CUBIC
CUBIC 是 Linux 多年来的默认 TCP 拥塞控制算法。它的设计目标是:
- 在高带宽、高 RTT 网络中快速恢复窗口;
- 在丢包后平滑降低窗口,减少抖动;
- 保持对 Reno 的友好性。
AI 场景:从对象存储拉取大模型 checkpoint、跨节点传输大日志、HTTP 推理服务的长连接,都可能受 CUBIC 影响。
6.2 CUBIC 的核心思想
CUBIC 把拥塞窗口 cwnd 看作时间 t 的立方函数:
W(t) = C * (t - K)^3 + WmaxWmax:上一次丢包时的窗口大小;K:窗口从Wmax降到 0 所需的时间;C:常数,控制增长激进程度。
丢包后,窗口乘性减少(通常降到 0.7 * Wmax),然后按立方函数重新增长。
6.3 Linux 源码入口
CUBIC 实现在 net/ipv4/tcp_cubic.c。
关键结构体
c
// include/net/tcp.h
struct tcp_sock {
u32 snd_cwnd; // 拥塞窗口
u32 snd_ssthresh; // 慢启动阈值
...
};cubic 私有数据
c
// net/ipv4/tcp_cubic.c
struct bictcp {
u32 cnt; // 每 ACK 增加 cwnd 的计数
u32 last_max_cwnd; // 上一次最大窗口
u32 last_cwnd; // 上一次 cwnd
u32 last_time; // 上一次更新时间
u32 bic_origin_point;
u32 bic_K;
s32 bic_tcp_cwnd;
u32 ack_cnt;
...
};拥塞窗口更新
核心函数 bictcp_update:
c
static inline void bictcp_update(struct bictcp *ca, u32 cwnd, u32 acked)
{
u32 delta, bic_target, offs;
u64 t;
ca->ack_cnt += acked;
if (ca->last_cwnd == cwnd &&
(s32)(tcp_jiffies32 - ca->last_time) <= HZ / 20)
return;
ca->last_cwnd = cwnd;
ca->last_time = tcp_jiffies32;
if (ca->epoch_start == 0) {
ca->epoch_start = tcp_jiffies32;
ca->ack_cnt = acked;
ca->tcp_cwnd = cwnd;
if (ca->last_max_cwnd <= cwnd) {
ca->bic_K = 0;
ca->bic_origin_point = cwnd;
} else {
ca->bic_origin_point = ca->last_max_cwnd;
delta = ca->last_max_cwnd - cwnd;
do_div(delta, BICTCP_B);
ca->bic_K = cube_root(delta);
}
}
t = (s32)(tcp_jiffies32 - ca->epoch_start);
t += msecs_to_jiffies(ca->delay_min);
if (t > ca->bic_K)
offs = t - ca->bic_K;
else
offs = ca->bic_K - t;
delta = cube(offs);
if (t > ca->bic_K)
bic_target = ca->bic_origin_point + delta;
else
bic_target = ca->bic_origin_point - delta;
if (bic_target > cwnd)
ca->cnt = cwnd / (bic_target - cwnd);
else
ca->cnt = 100 * cwnd;
}这段代码计算当前时间 t 距离 K 的偏移,然后根据立方函数计算目标窗口 bic_target,最后决定 cnt(每收到多少个 ACK 增加一次 cwnd)。
丢包处理
c
static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
{
...
if (tcp_in_slow_start(tp)) {
// 慢启动阶段:指数增长
acked = tcp_slow_start(tp, acked);
if (!acked)
return;
}
bictcp_update(ca, tp->snd_cwnd, acked);
tcp_cong_avoid_ai(sk, ca->cnt, acked);
}丢包后,CUBIC 进入拥塞避免阶段,按立方函数增长窗口。
6.4 与 AI 场景的关系
大文件传输
CUBIC 在高 BDP 链路上恢复窗口较快,但如果频繁丢包,窗口震荡会导致吞吐不稳定。
推理服务
长连接下,CUBIC 的窗口增长和降窗会影响延迟。BBR 或 QUIC 可能更适合低延迟推理。
RDMA 网络
RoCEv2 不跑 TCP,但同样面临拥塞控制问题。数据中心常用 DCTCP 或基于 ECN 的拥塞控制。
6.5 阅读网络源码的建议
- 抓主线:不要陷入每个宏和分支;
- 用工具:elixir.bootlin.com、GitHub 搜索;
- 看版本:不同内核版本差异大;
- 配合文档:Kernel Documentation 和 RFC;
- 动手实验:用
sysctl net.ipv4.tcp_congestion_control切换算法,观察 iperf 结果。
6.6 本节小结
- CUBIC 用立方函数控制拥塞窗口增长;
- 丢包后窗口乘性减少,然后按立方函数重新增长;
- Linux 实现在
net/ipv4/tcp_cubic.c,核心函数bictcp_update; - CUBIC 影响大文件传输和 TCP 长连接推理服务;
- RDMA 网络有独立的拥塞控制机制(PFC/ECN/DCTCP)。
下一节进入工程实践:用 Python 写一个网络机制模拟器。