犀牛鸟 2026 研究笔记 estelledc.github.io

第 13 章:犀牛鸟竞赛实战指南

竞赛速查(P1 竞赛速通者先看这里) ### 调研元信息 | 项目 | 值 | |------|-----| | 调研时间 | 2026-06-24 | | 目标项目 | TQUIC(腾讯开源 QUIC 协议实现) | | 主要语言 | Rust | | 竞赛形式 | 开源贡献(提交 PR) | | 活跃度警告 | 最近半年无 push(最后 ~2025.12),参赛前确认 issue 响应 | ### 环境矩阵 | 环境 | 能做什么 | 不能做什么 / 注意 | |------|---------|-------------------| | Linux (2C4G+ 推荐) | 完整编译 + `cargo test` + `tc netem` 网络模拟 + perf | — | | Mac | 编译 + 单测 + 基本开发 | 无 `tc netem`(无法做网络损伤测试) | | 云服务器 | 同 Linux + 多机组网测试 | 费用;建议用 2 台以上做端到端 | ### 贡献路径分级 | 级别 | 方向 | 预估工时 | 竞赛价值 | |------|------|---------|---------| | L1 零门槛 | 文档/typo/cargo clippy 修复 | 2-3 天 | 低(热身+破冰) | | L2 低门槛 | 补全单元测试 / bench 用例 | 1 周 | 中 | | L3 中门槛 | 多路径调度器改进(MinRTT 基线 +10%) | 2-3 周 | 中高 | | L4 高门槛 | 自定义拥塞控制算法实现 | 4+ 周 | 高 | ### 第一周行动清单 | 天 | 目标 | 产出 | |----|------|------| | Day 1 | `cargo build` + `cargo test` 通过 + `cargo doc --open` | 编译成功 + 本地文档 | | Day 2 | 画模块依赖图 + 找到 connection 入口 | 手绘架构图 | | Day 3 | 追踪 send/receive 路径 + 加 logging | 理解数据流 | | Day 4 | 精读目标模块(调度器 or 拥塞控制)| 确定贡献方向 | | Day 5 | 提交第一个 PR(typo/lint/小测试) | 破冰 + 熟悉 CI | ### 已知风险与边界 | 风险 | 应对 | |------|------| | TQUIC 近半年不活跃 | 先提 issue 观察 1-2 周响应;无回复则考虑换赛道 | | Rust 学习曲线 | 至少需要《The Rust Book》前 10 章基础 | | 过度复杂化首版(DRL/GPU 模型) | 先超越 MinRTT 基线 10% 再加复杂度 | | 无基线对比 | 每个改进必须有量化 benchmark(A/B 对比) | ### 读完你应能 - [ ] 用 `cargo build` 编译 TQUIC 并跑通全部单测 - [ ] 说清楚 QUIC 相比 TCP 的三个核心优势 - [ ] 画出 TQUIC 的模块依赖图(connection / stream / frame / scheduler) - [ ] 用 `tc netem` 设置丢包/延迟环境并跑 benchmark - [ ] 解释多路径调度的 MinRTT 策略及其局限

从游戏攻略说起

想象你要参加一场「城市建设」游戏大赛:

新手的做法

高手的做法

开源竞赛也是一样——不是上来就写代码,而是先理解项目、理解评分、理解社区,然后有节奏地推进。

本章就是你的”犀牛鸟竞赛攻略”。


13.1 竞赛全景:犀牛鸟 2026 与 TQUIC

13.1.1 什么是犀牛鸟开源竞赛

犀牛鸟(Rhino-Bird)是腾讯开源联合高校举办的开源人才培养计划。核心模式:

学生 ──> 选择开源项目 ──> 导师指导 ──> 完成任务 ──> 提交成果 ──> 评审打分
                │                │              │
                ▼                ▼              ▼
           项目理解          技术深入       代码贡献
           社区互动          方案设计       文档完善
           问题发现          实现验证       成果展示

13.1.2 TQUIC 项目在竞赛中的定位

TQUIC 是腾讯开源的 QUIC 协议实现,用 Rust 编写。在竞赛中,它通常会出以下方向的题目:

方向一:多路径调度算法优化

方向二:拥塞控制改进

方向三:协议扩展

方向四:性能优化

13.1.3 竞赛评分维度(通用)

┌──────────────────────────────────────────────────────────────┐
│                    竞赛评分框架                                │
├──────────────┬───────────┬───────────────────────────────────┤
│   维度       │   权重    │   说明                            │
├──────────────┼───────────┼───────────────────────────────────┤
│ 技术深度     │   30%     │ 方案是否有技术含量                │
│ 完成度       │   25%     │ 代码是否可用、测试是否通过        │
│ 创新性       │   20%     │ 是否有新思路、新方法              │
│ 文档质量     │   15%     │ 设计文档、使用说明是否清晰        │
│ 社区贡献     │   10%     │ PR 质量、Issue 讨论、代码风格     │
└──────────────┴───────────┴───────────────────────────────────┘

关键洞察:完成度 + 文档质量 = 40%。很多同学过于追求技术深度和创新性,忽略了”把东西做完并写清楚”这个基本功。一个 80% 完成度的方案远胜过一个 40% 完成度但更”创新”的方案。


13.2 赛前准备:从零到能动手(第 1-2 周)

13.2.1 环境搭建清单

# 1. Rust 工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default stable
rustup component add clippy rustfmt

# 2. 克隆 TQUIC
git clone https://github.com/Tencent/tquic.git
cd tquic

# 3. 构建验证
cargo build
cargo test

# 4. 运行示例(如果有)
cargo run --example server
cargo run --example client

# 5. 开发工具
cargo install cargo-watch    # 自动重编译
cargo install cargo-flamegraph  # 性能分析
cargo install cargo-criterion   # 基准测试

13.2.2 代码阅读路线图

不要一上来就读全部代码。按这个顺序:

第 1 天:宏观结构
├── 读 README.md、CONTRIBUTING.md
├── 看 Cargo.toml 了解模块划分
├── 跑 `cargo doc --open` 看 API 文档
└── 画出模块依赖图

第 2-3 天:核心数据流
├── 找到连接建立的入口
├── 跟踪一个数据包从发送到接收的完整路径
├── 理解 Connection 结构体的字段含义
└── 找到事件驱动的 poll/process 循环

第 4-5 天:你的方向
├── 如果是多路径:读 MultipathScheduler trait + 现有实现
├── 如果是拥塞控制:读 CongestionController trait + 算法
├── 如果是协议扩展:读 frame 编解码 + 状态机
└── 记录不理解的点,准备问导师

第 6-7 天:动手实验
├── 写一个最简单的修改(如加一行日志)
├── 跑测试确认没破坏东西
├── 尝试写一个最小的 benchmark
└── 提交一个 typo-fix PR 熟悉流程

13.2.3 必读材料优先级

优先级 P0(必须读):
├── TQUIC 的 README + 架构文档
├── RFC 9000(QUIC 核心)的 1-4 章
├── 你的竞赛题目描述和评分标准
└── 导师给的参考论文(如果有)

优先级 P1(应该读):
├── RFC 9000 全文
├── RFC 9002(丢失检测和拥塞控制)
├── 多路径 QUIC draft(如果相关)
└── TQUIC 的现有 PR 和 Issue

优先级 P2(有空就读):
├── 其他 QUIC 实现的架构文档
├── 相关学术论文
├── IETF 邮件列表讨论
└── 网络协议教材的相关章节

13.2.4 与导师的第一次沟通

准备一份一页纸的沟通提纲:

## 初次沟通提纲

### 我的背景
- 编程经验:[如实说]
- Rust 水平:[如实说]
- 网络协议基础:[如实说]

### 我对题目的理解
- 题目要求:[用自己的话复述]
- 我理解的关键难点:[列 2-3 个]
- 我的初步想法:[即使不成熟也说]

### 我想确认的问题
1. 评分最看重什么?
2. 有推荐的参考实现或论文吗?
3. 期望的最终交付物是什么形式?
4. 中间检查点是什么时候?交付什么?

### 我的时间计划
- 第 1-2 周:[环境搭建 + 代码阅读]
- 第 3-4 周:[方案设计]
- 第 5-8 周:[实现]
- 第 9-10 周:[测试优化 + 文档]

13.3 方案设计:想清楚再动手(第 3-4 周)

13.3.1 好方案的特征

好方案                              烂方案
─────────────────────────────────────────────────────────
明确的问题定义                      "我要优化调度"
可衡量的目标                        "提升性能"
清晰的技术路线                      "用 AI 解决"
考虑了边界情况                      只考虑理想场景
有 fallback 计划                    只有一条路
预估了工作量                        "应该不难吧"

13.3.2 方案设计模板

# [方案名称] 设计文档

## 1. 问题定义
### 1.1 背景
- 当前 TQUIC 的 [X] 在 [Y] 场景下表现不佳
- 具体表现:[用数据说话]

### 1.2 目标
- 主目标:在 [场景] 下,将 [指标] 从 [当前值] 提升到 [目标值]
- 次目标:不降低 [其他指标] 超过 [X]%
- 约束:不修改公共 API、通过现有测试

## 2. 现状分析
### 2.1 现有实现
- [描述当前实现的方式]
- [为什么在目标场景下表现不好]

### 2.2 业界方案
- 方案 A:[描述],优点:[X],缺点:[Y]
- 方案 B:[描述],优点:[X],缺点:[Y]
- 方案 C:[描述],优点:[X],缺点:[Y]

## 3. 方案设计
### 3.1 整体架构
[ASCII 图]

### 3.2 关键算法
[伪代码或公式]

### 3.3 数据结构
[新增/修改的结构体]

### 3.4 接口设计
[trait 定义或 API 签名]

## 4. 实现计划
### 4.1 分阶段实现
- Phase 1(第 X 周):[最小可验证版本]
- Phase 2(第 X 周):[完整功能]
- Phase 3(第 X 周):[优化 + 边界处理]

### 4.2 测试计划
- 单元测试:[覆盖哪些函数]
- 集成测试:[覆盖哪些场景]
- 性能测试:[用什么工具、测什么指标]

## 5. 风险与 Fallback
- 风险 1:[描述],应对:[措施]
- 风险 2:[描述],应对:[措施]
- 最坏情况:如果 [X] 不可行,退回到 [Y]

13.3.3 以”多路径调度优化”为例的方案思路

假设题目是”为 TQUIC 实现一个自适应多路径调度器”:

问题分析

当前 MinRtt 调度器的局限:
┌─────────────────────────────────────────────────────────────┐
│  MinRtt 策略:总是选 RTT 最小的路径发包                      │
│                                                             │
│  问题 1:RTT 最小 ≠ 吞吐最大(带宽可能很小)               │
│  问题 2:测量滞后——当很多包涌入"最快"路径时,它变慢了       │
│  问题 3:没有利用其他路径的空闲容量                          │
│  问题 4:突变网络(如 Wi-Fi→蜂窝切换)反应慢                │
└─────────────────────────────────────────────────────────────┘

方案选项对比

┌─────────────┬────────────────┬───────────────┬──────────────┐
│  方案       │ 核心思想       │ 优势          │ 劣势         │
├─────────────┼────────────────┼───────────────┼──────────────┤
│ 加权轮转    │ 按带宽比例分配 │ 简单可预测    │ 不自适应     │
│ BLEST       │ 避免 HoL      │ 减少延迟      │ 不最大化吞吐 │
│ ECF         │ 最早完成优先   │ 理论最优      │ 需要精确估计 │
│ UCB/MAB     │ 探索与利用     │ 自适应        │ 收敛慢       │
│ DRL         │ 深度强化学习   │ 复杂场景强    │ 训练难部署难 │
└─────────────┴────────────────┴───────────────┴──────────────┘

推荐的渐进式方案

Phase 1:加权 MinRtt(第 5 周)
─────────────────────────────────
- 在 MinRtt 基础上加入带宽权重
- 调度分数 = α × (1/RTT) + β × bandwidth
- 验证:比纯 MinRtt 在异构网络下提升 > 10%

Phase 2:带探索的自适应调度(第 6-7 周)
─────────────────────────────────
- 引入 UCB1 算法的探索机制
- 定期在次优路径上发少量探测包
- 根据实际性能动态调整权重
- 验证:在网络突变场景下恢复时间 < 2 RTT

Phase 3:HoL 感知(第 8 周)
─────────────────────────────────
- 考虑接收端重排序缓冲区状态
- 如果某路径 RTT 波动大,减少在它上面发首包
- 验证:P99 延迟降低 > 20%

Fallback:如果 Phase 2 效果不好
─────────────────────────────────
- 退回到 Phase 1 的加权方案
- 把 Phase 2 的分析作为"未来工作"写入文档
- 确保 Phase 1 是稳定可用的

13.3.4 方案评审 Checklist

在提交方案给导师前,自检:

□ 问题定义明确吗?有数据支撑吗?
□ 目标可衡量吗?"提升性能"不算,"吞吐提升 15%"算
□ 考虑了至少 3 个候选方案吗?说明了为什么选这个?
□ 有分阶段计划吗?每阶段有可交付物吗?
□ Phase 1 足够简单吗?能在 1 周内完成吗?
□ 有 Fallback 计划吗?最坏情况还能交付什么?
□ 测试计划明确吗?怎么证明方案有效?
□ 风险列出来了吗?各有应对措施吗?
□ 时间估计合理吗?留了 buffer 吗?(建议留 20% buffer)
□ 代码量估计了吗?大概多少行新代码?

13.4 实现阶段:写代码的节奏(第 5-8 周)

13.4.1 代码组织原则

以实现一个新的调度器为例:

tquic/src/
├── multipath/
│   ├── scheduler/
│   │   ├── mod.rs              # trait 定义 + 工厂函数
│   │   ├── min_rtt.rs          # 现有:MinRtt 调度器
│   │   ├── round_robin.rs      # 现有:轮转调度器
│   │   ├── redundant.rs        # 现有:冗余调度器
│   │   └── adaptive.rs         # ★ 新增:你的自适应调度器
│   └── ...
├── ...

新增文件的结构

//! 自适应多路径调度器
//!
//! 基于 UCB1 算法在多条路径间进行探索与利用的平衡,
//! 同时考虑 RTT 和带宽两个维度的路径质量。
//!
//! # 算法概述
//! 1. 每条路径维护一个"得分",综合 RTT、带宽、探索奖励
//! 2. 每次调度选得分最高的路径
//! 3. 定期更新得分,衰减旧测量值
//!
//! # 参考
//! - [UCB1] Auer et al., "Finite-time Analysis of the Multiarmed Bandit Problem"
//! - [BLEST] Ferlin et al., "BLEST: Blocking Estimation-based MPTCP Scheduler"

use std::time::{Duration, Instant};
use crate::multipath::path::PathId;
use crate::multipath::scheduler::MultipathScheduler;
use crate::connection::Connection;

/// 路径统计信息,用于调度决策
#[derive(Debug, Clone)]
struct PathStats {
    /// 平滑 RTT(指数移动平均)
    smoothed_rtt: Duration,
    /// 估计带宽(bytes/sec)
    estimated_bandwidth: u64,
    /// 该路径被选中的总次数
    selection_count: u64,
    /// 该路径的累计"奖励"
    cumulative_reward: f64,
    /// 上次更新时间
    last_updated: Instant,
}

/// 自适应调度器的配置参数
#[derive(Debug, Clone)]
pub struct AdaptiveConfig {
    /// UCB 探索系数 c,越大越倾向探索
    /// 推荐范围:0.5 - 2.0,默认 1.0
    pub exploration_coefficient: f64,
    /// RTT 权重 α
    pub rtt_weight: f64,
    /// 带宽权重 β
    pub bandwidth_weight: f64,
    /// 测量值衰减因子(每秒衰减多少)
    pub decay_factor: f64,
    /// 最小探测间隔
    pub min_probe_interval: Duration,
}

impl Default for AdaptiveConfig {
    fn default() -> Self {
        Self {
            exploration_coefficient: 1.0,
            rtt_weight: 0.6,
            bandwidth_weight: 0.4,
            decay_factor: 0.95,
            min_probe_interval: Duration::from_millis(100),
        }
    }
}

/// 自适应多路径调度器
pub struct AdaptiveScheduler {
    config: AdaptiveConfig,
    path_stats: HashMap<PathId, PathStats>,
    total_selections: u64,
}

impl AdaptiveScheduler {
    pub fn new(config: AdaptiveConfig) -> Self {
        Self {
            config,
            path_stats: HashMap::new(),
            total_selections: 0,
        }
    }

    /// 计算路径的 UCB 得分
    /// 得分 = 利用项 + 探索项
    /// 利用项 = α × RTT得分 + β × 带宽得分
    /// 探索项 = c × sqrt(ln(N) / n_i)
    fn compute_score(&self, path_id: PathId) -> f64 {
        let stats = match self.path_stats.get(&path_id) {
            Some(s) => s,
            None => return f64::MAX, // 未探索的路径优先
        };

        // 利用项:归一化的路径质量
        let rtt_score = 1.0 / stats.smoothed_rtt.as_secs_f64().max(0.001);
        let bw_score = stats.estimated_bandwidth as f64;
        let exploitation = self.config.rtt_weight * rtt_score
            + self.config.bandwidth_weight * bw_score;

        // 探索项:UCB1 公式
        let exploration = if stats.selection_count > 0 && self.total_selections > 0 {
            self.config.exploration_coefficient
                * ((self.total_selections as f64).ln() / stats.selection_count as f64).sqrt()
        } else {
            f64::MAX
        };

        exploitation + exploration
    }

    /// 更新路径统计(在收到 ACK 后调用)
    fn update_path_stats(&mut self, path_id: PathId, rtt: Duration, bytes_acked: u64) {
        let stats = self.path_stats.entry(path_id).or_insert(PathStats {
            smoothed_rtt: rtt,
            estimated_bandwidth: 0,
            selection_count: 0,
            cumulative_reward: 0.0,
            last_updated: Instant::now(),
        });

        // EWMA 更新 RTT
        let alpha = 0.125;
        stats.smoothed_rtt = Duration::from_secs_f64(
            (1.0 - alpha) * stats.smoothed_rtt.as_secs_f64() + alpha * rtt.as_secs_f64(),
        );

        // 更新带宽估计
        if rtt > Duration::ZERO {
            let instant_bw = bytes_acked as f64 / rtt.as_secs_f64();
            stats.estimated_bandwidth =
                ((1.0 - alpha) * stats.estimated_bandwidth as f64 + alpha * instant_bw) as u64;
        }

        stats.last_updated = Instant::now();
    }
}

impl MultipathScheduler for AdaptiveScheduler {
    fn on_schedule(&mut self, conn: &Connection) -> Option<PathId> {
        let available_paths = conn.paths().available();

        if available_paths.is_empty() {
            return None;
        }

        // 选择得分最高的路径
        let best_path = available_paths
            .iter()
            .max_by(|a, b| {
                self.compute_score(*a)
                    .partial_cmp(&self.compute_score(*b))
                    .unwrap_or(std::cmp::Ordering::Equal)
            })
            .copied();

        if let Some(path_id) = best_path {
            self.total_selections += 1;
            if let Some(stats) = self.path_stats.get_mut(&path_id) {
                stats.selection_count += 1;
            }
        }

        best_path
    }
}

13.4.2 测试策略

测试金字塔:

            /\
           /  \     集成测试(端到端场景)
          /────\    5-10 个关键场景
         /      \
        /────────\  属性测试(边界/随机)
       /          \ 用 proptest crate
      /────────────\
     /              \ 单元测试(每个函数)
    /────────────────\ 覆盖正常 + 异常路径

单元测试示例

#[cfg(test)]
mod tests {
    use super::*;

    /// 未探索的路径应该优先被选中
    #[test]
    fn test_unexplored_path_priority() {
        let mut scheduler = AdaptiveScheduler::new(AdaptiveConfig::default());

        // 模拟 path_0 已经被选了 100 次
        scheduler.path_stats.insert(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(50),
            estimated_bandwidth: 1_000_000,
            selection_count: 100,
            cumulative_reward: 80.0,
            last_updated: Instant::now(),
        });

        // path_1 是新路径,从未被探索
        // 它的得分应该是 f64::MAX,所以应该被优先选中

        let score_0 = scheduler.compute_score(PathId(0));
        let score_1 = scheduler.compute_score(PathId(1));

        assert!(score_1 > score_0, "未探索路径应有更高得分");
    }

    /// RTT 更小的路径在利用阶段应该得分更高
    #[test]
    fn test_lower_rtt_higher_exploitation_score() {
        let mut scheduler = AdaptiveScheduler::new(AdaptiveConfig {
            exploration_coefficient: 0.0, // 关闭探索,只看利用
            ..Default::default()
        });

        scheduler.path_stats.insert(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(20),
            estimated_bandwidth: 1_000_000,
            selection_count: 50,
            cumulative_reward: 40.0,
            last_updated: Instant::now(),
        });

        scheduler.path_stats.insert(PathId(1), PathStats {
            smoothed_rtt: Duration::from_millis(100),
            estimated_bandwidth: 1_000_000,
            selection_count: 50,
            cumulative_reward: 30.0,
            last_updated: Instant::now(),
        });

        scheduler.total_selections = 100;

        let score_0 = scheduler.compute_score(PathId(0));
        let score_1 = scheduler.compute_score(PathId(1));

        assert!(score_0 > score_1, "RTT 更小的路径应得分更高");
    }

    /// 探索系数增大应该增加低探索路径的得分
    #[test]
    fn test_exploration_coefficient_effect() {
        let mut scheduler_low = AdaptiveScheduler::new(AdaptiveConfig {
            exploration_coefficient: 0.5,
            ..Default::default()
        });
        let mut scheduler_high = AdaptiveScheduler::new(AdaptiveConfig {
            exploration_coefficient: 2.0,
            ..Default::default()
        });

        let stats = PathStats {
            smoothed_rtt: Duration::from_millis(50),
            estimated_bandwidth: 1_000_000,
            selection_count: 5,  // 很少被选
            cumulative_reward: 3.0,
            last_updated: Instant::now(),
        };

        scheduler_low.path_stats.insert(PathId(0), stats.clone());
        scheduler_low.total_selections = 100;

        scheduler_high.path_stats.insert(PathId(0), stats);
        scheduler_high.total_selections = 100;

        let score_low = scheduler_low.compute_score(PathId(0));
        let score_high = scheduler_high.compute_score(PathId(0));

        assert!(
            score_high > score_low,
            "更高的探索系数应给低探索路径更高得分"
        );
    }

    /// RTT 更新应该是指数移动平均
    #[test]
    fn test_rtt_ewma_update() {
        let mut scheduler = AdaptiveScheduler::new(AdaptiveConfig::default());

        // 初始 RTT = 100ms
        scheduler.update_path_stats(PathId(0), Duration::from_millis(100), 1000);
        let rtt_1 = scheduler.path_stats[&PathId(0)].smoothed_rtt;

        // 新测量 RTT = 20ms(大幅下降)
        scheduler.update_path_stats(PathId(0), Duration::from_millis(20), 1000);
        let rtt_2 = scheduler.path_stats[&PathId(0)].smoothed_rtt;

        // EWMA 应该让 rtt_2 在 20 和 100 之间,且比 rtt_1 小
        assert!(rtt_2 < rtt_1, "新的小 RTT 应拉低平滑值");
        assert!(rtt_2 > Duration::from_millis(20), "EWMA 不会一步跳到新值");
    }
}

关键观察

13.2.2 里程碑 2:MinRTT 调度器

// 实现最简单但有效的调度策略
use std::collections::HashMap;
use std::time::Duration;

struct PathStats {
    smoothed_rtt: Duration,
    available_cwnd: usize,  // 拥塞窗口剩余空间
    is_active: bool,
}

struct MinRttScheduler {
    paths: HashMap<u64, PathStats>,
}

impl MinRttScheduler {
    fn select_path(&self) -> Option<u64> {
        // 只在有可用窗口的活跃路径中选择
        self.paths.iter()
            .filter(|(_, stats)| stats.is_active && stats.available_cwnd > 0)
            .min_by_key(|(_, stats)| stats.smoothed_rtt)
            .map(|(id, _)| *id)
    }

    fn on_packet_sent(&mut self, path_id: u64, size: usize) {
        if let Some(stats) = self.paths.get_mut(&path_id) {
            stats.available_cwnd = stats.available_cwnd.saturating_sub(size);
        }
    }

    fn on_ack_received(&mut self, path_id: u64, size: usize, rtt: Duration) {
        if let Some(stats) = self.paths.get_mut(&path_id) {
            // 释放窗口空间
            stats.available_cwnd += size;
            // 更新 RTT(简化版 EWMA)
            let alpha = 0.125;
            let new_rtt = Duration::from_secs_f64(
                (1.0 - alpha) * stats.smoothed_rtt.as_secs_f64()
                + alpha * rtt.as_secs_f64()
            );
            stats.smoothed_rtt = new_rtt;
        }
    }
}

为什么从 MinRTT 开始

13.2.3 里程碑 3:冗余调度器

// 冗余调度:同一数据发到所有路径
struct RedundantScheduler {
    paths: HashMap<u64, PathStats>,
}

impl RedundantScheduler {
    fn select_paths(&self) -> Vec<u64> {
        // 返回所有有窗口的活跃路径
        self.paths.iter()
            .filter(|(_, stats)| stats.is_active && stats.available_cwnd > 0)
            .map(|(id, _)| *id)
            .collect()
    }

    fn schedule_packet(&self, data: &[u8]) -> Vec<(u64, Vec<u8>)> {
        // 同一份数据复制到所有可用路径
        self.select_paths().into_iter()
            .map(|path_id| (path_id, data.to_vec()))
            .collect()
    }
}

冗余调度的价值

13.2.4 里程碑 4:自适应调度器

// 结合 RTT 和带宽的加权调度
struct AdaptiveScheduler {
    paths: HashMap<u64, PathStats>,
    // 每条路径的"评分"
    scores: HashMap<u64, f64>,
}

struct PathStats {
    smoothed_rtt: Duration,
    estimated_bandwidth: f64,  // bytes/sec
    loss_rate: f64,            // 0.0 - 1.0
    available_cwnd: usize,
    is_active: bool,
}

impl AdaptiveScheduler {
    fn update_scores(&mut self) {
        for (id, stats) in &self.paths {
            if !stats.is_active {
                self.scores.insert(*id, 0.0);
                continue;
            }

            // 综合评分 = 带宽 / (RTT * (1 + loss_rate))
            // 直觉:高带宽、低延迟、低丢包 = 高分
            let rtt_sec = stats.smoothed_rtt.as_secs_f64().max(0.001);
            let score = stats.estimated_bandwidth
                / (rtt_sec * (1.0 + stats.loss_rate * 10.0));

            self.scores.insert(*id, score);
        }
    }

    fn select_path(&mut self) -> Option<u64> {
        self.update_scores();

        self.paths.iter()
            .filter(|(_, stats)| stats.is_active && stats.available_cwnd > 0)
            .max_by(|(id_a, _), (id_b, _)| {
                let score_a = self.scores.get(id_a).unwrap_or(&0.0);
                let score_b = self.scores.get(id_b).unwrap_or(&0.0);
                score_a.partial_cmp(score_b).unwrap()
            })
            .map(|(id, _)| *id)
    }
}

渐进式改进


13.3 TQUIC 代码导读地图

13.3.1 项目结构概览

tquic/
├── src/
│   ├── connection/          # 连接管理
│   │   ├── mod.rs          # Connection 主结构
│   │   ├── path.rs         # 路径管理 ← 重点
│   │   ├── space.rs        # 包号空间
│   │   └── stream.rs       # 流管理
│   ├── congestion_control/  # 拥塞控制
│   │   ├── mod.rs          # CongestionController trait
│   │   ├── cubic.rs        # CUBIC 实现
│   │   ├── bbr/            # BBR 实现
│   │   │   ├── mod.rs
│   │   │   ├── bbr2.rs
│   │   │   └── bbr3.rs
│   │   └── copa.rs         # COPA 实现
│   ├── multipath/           # 多路径 ← 竞赛核心
│   │   ├── mod.rs          # MultipathScheduler trait
│   │   ├── minrtt.rs       # 最小 RTT 调度
│   │   ├── redundant.rs    # 冗余调度
│   │   └── roundrobin.rs   # 轮询调度
│   ├── recovery/            # 丢包恢复
│   ├── tls/                 # TLS 集成
│   └── lib.rs              # 公共 API
├── tools/                   # CLI 工具
├── ffi/                     # C FFI 绑定
└── benches/                 # 性能基准

13.3.2 核心 Trait 关系

┌─────────────────────────────────────────────────────┐
│                   Connection                         │
│                                                     │
│  ┌─────────────┐    ┌──────────────────────────┐   │
│  │  PathMap     │    │  MultipathScheduler      │   │
│  │             │    │  (trait object)           │   │
│  │  path_0 ────┼────┤                          │   │
│  │  path_1 ────┼────┤  fn on_packet_sent()     │   │
│  │  path_2 ────┼────┤  fn on_packet_acked()    │   │
│  │             │    │  fn select_send_path()   │   │
│  └─────────────┘    └──────────────────────────┘   │
│                                                     │
│  每条 Path 包含:                                    │
│  ┌──────────────────────────────────────────────┐  │
│  │  Path                                        │  │
│  │  ├── CongestionController (独立)             │  │
│  │  ├── RecoveryState (独立)                    │  │
│  │  ├── smoothed_rtt                            │  │
│  │  ├── cwnd / bytes_in_flight                  │  │
│  │  └── peer_addr / local_addr                  │  │
│  └──────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘

13.3.3 阅读路径建议

第一遍(2 天):理解接口

  1. src/multipath/mod.rs — 读 MultipathScheduler trait 定义
  2. src/multipath/minrtt.rs — 最简实现,对照 trait 方法
  3. src/connection/path.rsPathMap 如何管理多条路径

第二遍(3 天):理解数据流

  1. src/connection/mod.rssend() 如何调用 scheduler
  2. src/recovery/ — ACK 如何回传到 path stats
  3. src/congestion_control/mod.rs — CC trait 与 Path 的关系

第三遍(5 天):准备修改

  1. 跑通 cargo test 中 multipath 相关测试
  2. 加 tracing 日志观察调度决策过程
  3. 尝试写一个新的 scheduler(下节详细说)

13.4 实现自定义调度器

13.4.1 步骤总览

┌──────────────────────────────────────────────────────┐
│  实现新调度器的 6 个步骤                               │
├──────────────────────────────────────────────────────┤
│                                                      │
│  1. Fork tquic 仓库                                  │
│     └── git clone + cargo build 验证                 │
│                                                      │
│  2. 创建新文件 src/multipath/my_scheduler.rs         │
│     └── impl MultipathScheduler for MyScheduler      │
│                                                      │
│  3. 在 mod.rs 中注册                                 │
│     └── pub mod my_scheduler; + 配置项               │
│                                                      │
│  4. 写单元测试                                       │
│     └── 模拟多路径场景验证行为                        │
│                                                      │
│  5. 集成测试                                         │
│     └── 用 tquic 工具跑真实连接                       │
│                                                      │
│  6. 性能评估                                         │
│     └── 对比现有调度器的吞吐/延迟/公平性              │
│                                                      │
└──────────────────────────────────────────────────────┘

13.4.2 Trait 实现模板

// src/multipath/weighted_rtt.rs
// 加权 RTT 调度器:考虑 RTT 和可用窗口的比例

use super::{MultipathScheduler, SchedulerContext};
use crate::connection::path::PathId;

pub struct WeightedRttScheduler {
    // 配置参数
    rtt_weight: f64,    // RTT 在评分中的权重
    cwnd_weight: f64,   // 可用窗口在评分中的权重
}

impl WeightedRttScheduler {
    pub fn new() -> Self {
        Self {
            rtt_weight: 0.7,
            cwnd_weight: 0.3,
        }
    }

    fn score_path(&self, ctx: &SchedulerContext, path_id: PathId) -> f64 {
        let path = match ctx.get_path(path_id) {
            Some(p) => p,
            None => return 0.0,
        };

        if !path.is_active() || path.available_cwnd() == 0 {
            return 0.0;
        }

        // RTT 分数:越低越好,取倒数
        let rtt_score = 1.0 / path.smoothed_rtt().as_secs_f64().max(0.001);

        // 窗口分数:可用空间占比
        let cwnd_score = path.available_cwnd() as f64
            / path.max_cwnd() as f64;

        // 加权综合
        self.rtt_weight * rtt_score + self.cwnd_weight * cwnd_score
    }
}

impl MultipathScheduler for WeightedRttScheduler {
    fn select_send_path(&self, ctx: &SchedulerContext) -> Option<PathId> {
        ctx.active_paths()
            .iter()
            .filter(|&&pid| {
                ctx.get_path(pid)
                    .map(|p| p.available_cwnd() > 0)
                    .unwrap_or(false)
            })
            .max_by(|&&a, &&b| {
                let score_a = self.score_path(ctx, a);
                let score_b = self.score_path(ctx, b);
                score_a.partial_cmp(&score_b).unwrap_or(std::cmp::Ordering::Equal)
            })
            .copied()
    }

    fn on_packet_sent(&mut self, _ctx: &SchedulerContext, _path_id: PathId, _size: usize) {
        // 基础版本不需要额外状态
        // 高级版本可以在这里更新发送统计
    }

    fn on_packet_acked(&mut self, _ctx: &SchedulerContext, _path_id: PathId, _size: usize) {
        // 基础版本不需要额外状态
        // 高级版本可以在这里更新确认统计
    }

    fn on_packet_lost(&mut self, _ctx: &SchedulerContext, _path_id: PathId, _size: usize) {
        // 基础版本不需要额外状态
        // 高级版本可以在这里降低路径评分
    }
}

13.4.3 测试策略

#[cfg(test)]
mod tests {
    use super::*;

    // 测试 1:基本选择——应选 RTT 最低的路径
    #[test]
    fn test_selects_lowest_rtt() {
        let mut ctx = MockSchedulerContext::new();
        ctx.add_path(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(50),
            available_cwnd: 1000,
            max_cwnd: 10000,
            is_active: true,
        });
        ctx.add_path(PathId(1), PathStats {
            smoothed_rtt: Duration::from_millis(100),
            available_cwnd: 1000,
            max_cwnd: 10000,
            is_active: true,
        });

        let scheduler = WeightedRttScheduler::new();
        assert_eq!(scheduler.select_send_path(&ctx), Some(PathId(0)));
    }

    // 测试 2:窗口耗尽时应跳过
    #[test]
    fn test_skips_exhausted_path() {
        let mut ctx = MockSchedulerContext::new();
        ctx.add_path(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(10),  // 最低 RTT
            available_cwnd: 0,                         // 但窗口用完了
            max_cwnd: 10000,
            is_active: true,
        });
        ctx.add_path(PathId(1), PathStats {
            smoothed_rtt: Duration::from_millis(100),
            available_cwnd: 5000,
            max_cwnd: 10000,
            is_active: true,
        });

        let scheduler = WeightedRttScheduler::new();
        // 即使 path 0 RTT 更低,窗口为 0 也不能选
        assert_eq!(scheduler.select_send_path(&ctx), Some(PathId(1)));
    }

    // 测试 3:大窗口可以弥补高 RTT
    #[test]
    fn test_large_cwnd_compensates_high_rtt() {
        let mut ctx = MockSchedulerContext::new();
        // 路径 0:RTT 低但窗口快满了
        ctx.add_path(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(20),
            available_cwnd: 100,     // 只剩 1%
            max_cwnd: 10000,
            is_active: true,
        });
        // 路径 1:RTT 高但窗口很空
        ctx.add_path(PathId(1), PathStats {
            smoothed_rtt: Duration::from_millis(80),
            available_cwnd: 9000,    // 90% 空闲
            max_cwnd: 10000,
            is_active: true,
        });

        // 用高窗口权重的调度器
        let scheduler = WeightedRttScheduler {
            rtt_weight: 0.3,
            cwnd_weight: 0.7,
        };
        // 窗口权重高时,应该选窗口更空的路径 1
        assert_eq!(scheduler.select_send_path(&ctx), Some(PathId(1)));
    }

    // 测试 4:所有路径不可用时返回 None
    #[test]
    fn test_no_available_path() {
        let mut ctx = MockSchedulerContext::new();
        ctx.add_path(PathId(0), PathStats {
            smoothed_rtt: Duration::from_millis(50),
            available_cwnd: 0,
            max_cwnd: 10000,
            is_active: true,
        });
        ctx.add_path(PathId(1), PathStats {
            smoothed_rtt: Duration::from_millis(100),
            available_cwnd: 0,
            max_cwnd: 10000,
            is_active: false,
        });

        let scheduler = WeightedRttScheduler::new();
        assert_eq!(scheduler.select_send_path(&ctx), None);
    }
}

13.5 评估方法论

13.5.1 测试环境搭建

┌──────────────────────────────────────────────────────────────┐
│  评估环境架构                                                 │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────┐     tc netem      ┌─────────┐                 │
│  │         │◄──────────────────►│         │                 │
│  │  TQUIC  │     路径 1         │  TQUIC  │                 │
│  │  Client │     (WiFi模拟)     │  Server │                 │
│  │         │                    │         │                 │
│  │         │     tc netem      │         │                 │
│  │         │◄──────────────────►│         │                 │
│  └─────────┘     路径 2         └─────────┘                 │
│                  (4G模拟)                                    │
│                                                              │
│  工具:                                                      │
│  - tc netem: 模拟延迟、丢包、带宽限制                         │
│  - netns: Linux 网络命名空间隔离路径                          │
│  - iperf3: 基准带宽测量                                      │
│  - wireshark: 抓包分析                                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

13.5.2 场景设计

#!/bin/bash
# setup-test-env.sh - 创建双路径测试环境

# 场景 1:对称路径(两条路径差不多)
setup_symmetric() {
    # 路径 1:50ms RTT, 10Mbps, 0% loss
    tc qdisc add dev veth0 root netem delay 25ms rate 10mbit
    # 路径 2:60ms RTT, 10Mbps, 0% loss
    tc qdisc add dev veth1 root netem delay 30ms rate 10mbit
}

# 场景 2:非对称路径(一快一慢)
setup_asymmetric() {
    # 路径 1:20ms RTT, 50Mbps, 0% loss (WiFi)
    tc qdisc add dev veth0 root netem delay 10ms rate 50mbit
    # 路径 2:100ms RTT, 5Mbps, 1% loss (4G)
    tc qdisc add dev veth1 root netem delay 50ms rate 5mbit loss 1%
}

# 场景 3:动态变化(路径质量交替变化)
setup_dynamic() {
    # 初始:路径 1 好
    tc qdisc add dev veth0 root netem delay 10ms rate 50mbit
    tc qdisc add dev veth1 root netem delay 100ms rate 5mbit

    # 30 秒后:路径 1 变差,路径 2 变好
    sleep 30
    tc qdisc change dev veth0 root netem delay 200ms rate 2mbit loss 5%
    tc qdisc change dev veth1 root netem delay 20ms rate 30mbit
}

# 场景 4:极端丢包
setup_lossy() {
    # 路径 1:低延迟但高丢包
    tc qdisc add dev veth0 root netem delay 10ms rate 50mbit loss 10%
    # 路径 2:高延迟但稳定
    tc qdisc add dev veth1 root netem delay 80ms rate 20mbit loss 0.1%
}

13.5.3 指标收集

// 评估指标结构
struct BenchmarkResults {
    // 吞吐量
    total_bytes: u64,
    duration: Duration,
    goodput_mbps: f64,          // 有效吞吐(排除重传)

    // 延迟
    latency_p50: Duration,      // 中位数延迟
    latency_p95: Duration,      // 95 分位
    latency_p99: Duration,      // 99 分位
    jitter: Duration,           // 延迟抖动

    // 效率
    retransmit_rate: f64,       // 重传率
    path_utilization: Vec<f64>, // 各路径利用率

    // 公平性
    jain_fairness_index: f64,   // Jain 公平性指数
}

impl BenchmarkResults {
    fn goodput(&self) -> f64 {
        (self.total_bytes as f64 * 8.0) / self.duration.as_secs_f64() / 1_000_000.0
    }

    fn path_balance(&self) -> f64 {
        // 路径利用率的标准差——越小越均衡
        let mean: f64 = self.path_utilization.iter().sum::<f64>()
            / self.path_utilization.len() as f64;
        let variance: f64 = self.path_utilization.iter()
            .map(|x| (x - mean).powi(2))
            .sum::<f64>() / self.path_utilization.len() as f64;
        variance.sqrt()
    }
}

13.5.4 结果呈现

┌─────────────────────────────────────────────────────────────────┐
│  评估结果对比表(示例)                                          │
├───────────────┬──────────┬──────────┬───────────┬──────────────┤
│  调度器        │ 吞吐 Mbps│ P95 延迟  │ 重传率 %  │ 路径均衡度   │
├───────────────┼──────────┼──────────┼───────────┼──────────────┤
│  MinRTT       │  45.2    │  62ms    │  2.1%     │  0.35        │
│  RoundRobin   │  38.7    │  105ms   │  4.5%     │  0.05        │
│  Redundant    │  23.1    │  28ms    │  0.3%     │  0.02        │
│  WeightedRTT  │  48.9    │  55ms    │  1.8%     │  0.22        │
│  (你的方案)    │  ??      │  ??      │  ??       │  ??          │
├───────────────┴──────────┴──────────┴───────────┴──────────────┤
│  场景:非对称路径(WiFi 50Mbps + 4G 5Mbps)                      │
│  传输:100MB 文件下载                                            │
└─────────────────────────────────────────────────────────────────┘

13.6 犀牛鸟竞赛策略

13.6.1 时间线规划

┌──────────────────────────────────────────────────────────────────┐
│  犀牛鸟 2026 竞赛时间线(建议)                                    │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  第 1-2 周:基础准备                                              │
│  ├── 读完本导读 ch01-ch06                                        │
│  ├── 搭建 TQUIC 开发环境                                         │
│  ├── 跑通所有现有测试                                            │
│  └── 理解 MultipathScheduler trait                               │
│                                                                  │
│  第 3-4 周:深入理解                                              │
│  ├── 读完 ch07-ch12                                              │
│  ├── 精读 TQUIC 多路径代码                                       │
│  ├── 复现论文实验(MinRTT vs RoundRobin)                        │
│  └── 确定研究方向                                                │
│                                                                  │
│  第 5-8 周:核心实现                                              │
│  ├── 实现自定义调度器 v1                                          │
│  ├── 搭建评估环境                                                │
│  ├── 初步实验 + 调参                                             │
│  └── 迭代改进到 v2、v3                                           │
│                                                                  │
│  第 9-10 周:优化与完善                                           │
│  ├── 边界场景测试                                                │
│  ├── 性能优化                                                    │
│  ├── 代码整理 + 文档                                             │
│  └── 准备 PR                                                     │
│                                                                  │
│  第 11-12 周:提交与答辩                                          │
│  ├── 撰写技术报告                                                │
│  ├── 准备演示                                                    │
│  ├── 提交 PR + 回应 review                                       │
│  └── 答辩准备                                                    │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

13.6.2 研究方向建议

方向 A:自适应权重调度

核心思路:不同场景下,RTT、带宽、丢包率对调度决策的重要性不同。设计一个能自动调整权重的调度器。

// 思路伪代码
struct AdaptiveWeightScheduler {
    weights: [f64; 3],  // [rtt_weight, bw_weight, loss_weight]
    history: VecDeque<Decision>,
    reward_signal: f64,
}

impl AdaptiveWeightScheduler {
    fn adapt_weights(&mut self) {
        // 根据最近的调度效果调整权重
        // 如果选了某路径后吞吐提升 → 增加当时权重高的因素
        // 如果选了某路径后延迟恶化 → 降低当时权重高的因素
    }
}

优势:

方向 B:预测性调度

核心思路:不只看当前 RTT,还预测未来 RTT 趋势。如果某路径 RTT 在上升,提前切换。

// 思路伪代码
struct PredictiveScheduler {
    // 每条路径维护 RTT 趋势
    rtt_history: HashMap<PathId, VecDeque<(Instant, Duration)>>,
}

impl PredictiveScheduler {
    fn predict_rtt(&self, path_id: PathId, horizon: Duration) -> Duration {
        // 用线性回归或指数平滑预测未来 RTT
        // 如果趋势在恶化,预测值 > 当前值
        // 如果趋势在改善,预测值 < 当前值
    }

    fn select_path(&self) -> Option<PathId> {
        // 用预测的 RTT 而不是当前 RTT 做决策
    }
}

优势:

方向 C:Explore-Exploit 调度

核心思路:借鉴多臂老虎机理论,平衡”利用已知最优路径”和”探索可能变好的路径”。

// 思路伪代码(UCB 策略)
struct UCBScheduler {
    path_rewards: HashMap<PathId, f64>,     // 平均收益
    path_counts: HashMap<PathId, u64>,      // 选择次数
    total_count: u64,
}

impl UCBScheduler {
    fn ucb_score(&self, path_id: PathId) -> f64 {
        let avg_reward = self.path_rewards[&path_id];
        let count = self.path_counts[&path_id] as f64;
        let exploration_bonus = (2.0 * (self.total_count as f64).ln() / count).sqrt();

        avg_reward + exploration_bonus  // 选择次数少的路径获得更高探索奖励
    }
}

优势:

13.6.3 评审关注点

根据开源竞赛的通常评审标准,评委通常关注:

┌─────────────────────────────────────────────────────────┐
│  评审维度及权重(推测)                                    │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  30%  技术创新                                          │
│  ├── 方案是否有新意?                                    │
│  ├── 不是简单搬运论文,有自己的改进                       │
│  └── 理论分析是否清晰?                                  │
│                                                         │
│  30%  实验效果                                          │
│  ├── 是否比现有方案更好?(至少在某些场景)               │
│  ├── 实验方法是否科学?                                  │
│  └── 结果是否可复现?                                    │
│                                                         │
│  20%  代码质量                                          │
│  ├── 是否符合项目编码规范?                              │
│  ├── 是否有完善的测试?                                  │
│  └── 是否易于维护和扩展?                                │
│                                                         │
│  20%  文档与展示                                        │
│  ├── 技术报告是否清晰?                                  │
│  ├── 是否有好的可视化?                                  │
│  └── 答辩是否能讲清思路?                                │
│                                                         │
└─────────────────────────────────────────────────────────┘

13.6.4 常见陷阱

陷阱 1:过度复杂化

错误做法:

正确做法:

陷阱 2:忽视基线对比

错误做法:

正确做法:

陷阱 3:不了解项目规范

错误做法:

正确做法:

陷阱 4:闭门造车

错误做法:

正确做法:


13.7 PR 提交指南

13.7.1 PR 结构

## 标题格式
multipath: add weighted RTT scheduler

## 描述模板
### What
简要说明这个 PR 做了什么。

### Why
为什么需要这个改进?现有方案有什么不足?

### How
技术方案简述。关键设计决策。

### Testing
- 单元测试:X 个新测试
- 集成测试:Y 个场景
- 性能测试:对比结果摘要

### Results
| 场景 | MinRTT | 本方案 | 提升 |
|------|--------|--------|------|
| 对称 | 45Mbps | 48Mbps | +6%  |
| 非对称| 42Mbps | 51Mbps | +21% |
| 动态 | 35Mbps | 44Mbps | +25% |

### Related
- RFC/论文引用
- 相关 Issue 链接

13.7.2 代码组织

# 好的 commit 历史
git log --oneline

abc1234 multipath: add WeightedRttScheduler implementation
def5678 multipath: add unit tests for WeightedRttScheduler
ghi9012 multipath: add integration tests for weighted scheduling
jkl3456 multipath: add benchmark comparing schedulers
mno7890 docs: add weighted RTT scheduler documentation
pqr1234 config: expose weighted scheduler configuration options

每个 commit:

13.7.3 Review 响应

收到 review 意见时:

┌──────────────────────────────────────────────────────┐
│  Review 响应最佳实践                                  │
├──────────────────────────────────────────────────────┤
│                                                      │
│  1. 感谢 reviewer 的时间                             │
│     "Thanks for the thorough review!"               │
│                                                      │
│  2. 对每条意见逐一回应                               │
│     - 同意并修改:"Good point, fixed in abc1234"    │
│     - 不同意但有理由:解释原因,提供数据              │
│     - 需要讨论:"I see your point, could we..."     │
│                                                      │
│  3. 不要 force push(除非 reviewer 要求 squash)     │
│                                                      │
│  4. 修改后主动 ping reviewer                         │
│     "Updated per your feedback, PTAL"               │
│                                                      │
└──────────────────────────────────────────────────────┘

13.8 学习资源索引

13.8.1 必读材料

RFC 与标准

论文

项目文档

13.8.2 辅助工具

┌─────────────────────────────────────────────────────────┐
│  开发工具链                                              │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  编程:                                                 │
│  ├── Rust (rustup + cargo)                             │
│  ├── rust-analyzer (IDE 支持)                          │
│  └── cargo-watch (自动重编译)                           │
│                                                         │
│  网络测试:                                             │
│  ├── tc netem (Linux 网络模拟)                          │
│  ├── mininet (网络拓扑模拟)                             │
│  ├── wireshark + quic 解析插件                          │
│  └── qlog (QUIC 日志标准格式)                           │
│                                                         │
│  性能分析:                                             │
│  ├── perf (Linux 性能分析)                              │
│  ├── flamegraph (火焰图)                                │
│  ├── criterion (Rust 基准测试)                          │
│  └── gnuplot / matplotlib (结果可视化)                  │
│                                                         │
│  协作:                                                 │
│  ├── git (版本管理)                                     │
│  ├── GitHub PR workflow                                 │
│  └── Markdown (文档)                                    │
│                                                         │
└─────────────────────────────────────────────────────────┘

13.8.3 社区资源


13.9 常见问题 FAQ

Q:我没有 Rust 经验,能参加吗?

A:可以。建议安排 2 周集中学 Rust 基础(所有权、生命周期、trait),然后边读 TQUIC 代码边学。TQUIC 的代码质量很高,是学 Rust 的好材料。关键是不要在学 Rust 上花太多时间——够用就行,重点是算法设计。

Q:选哪个方向最容易出成果?

A:方向 A(自适应权重)实现难度最低,改进空间大;方向 C(Explore-Exploit)理论性最强,容易写出好论文;方向 B(预测性)居中。建议根据自己的背景选:

Q:评估实验需要什么硬件?

A:一台 Linux 机器(或 VM)就够。用 tc netem 模拟网络条件不需要真实的多网卡。最低配置:2 核 CPU、4GB RAM。推荐:4 核、8GB。云服务器也行,但注意 tc netem 需要 root 权限。

Q:如何判断我的方案”足够好”?

A:三个标准:

  1. 在至少一个场景下比 MinRTT 有 10% 以上的改进
  2. 在其他场景下不比 MinRTT 差超过 5%
  3. 实现复杂度可控(代码 < 500 行)

满足这三条就是一个很好的竞赛成果。

Q:可以参考其他实现吗?

A:当然可以,但要注意:


13.10 竞赛心态

不要追求完美

  好的时间分配:          坏的时间分配:

  ┌──────────┐           ┌──────────┐
  │ 30% 调研 │           │ 60% 调研 │
  ├──────────┤           │          │
  │ 40% 实现 │           ├──────────┤
  │          │           │ 30% 实现 │
  ├──────────┤           ├──────────┤
  │ 20% 评估 │           │ 10% 评估 │
  ├──────────┤           └──────────┘
  │ 10% 文档 │
  └──────────┘

核心原则:

渐进式开发

v0.1:能编译通过的空壳
  ↓
v0.2:硬编码的简单逻辑
  ↓
v0.3:可配置的参数
  ↓
v0.4:自适应机制
  ↓
v0.5:边界处理和鲁棒性
  ↓
v1.0:完善的测试和文档

每个版本都是可演示的、可测试的。如果时间不够,v0.3 也是一个完整的成果。


本章小结

本章给出了从”读完导读”到”提交竞赛成果”的完整路径:

  1. 渐进式里程碑:从 RTT 测量开始,逐步构建调度器
  2. 代码导读地图:知道在 TQUIC 的哪里找什么
  3. 实现模板:可以直接用的 trait 实现骨架
  4. 评估方法:科学的实验设计和指标体系
  5. 竞赛策略:时间规划、方向选择、避坑指南
  6. PR 指南:从代码到提交的全流程

记住:竞赛的本质是”在有限时间内做出有价值的贡献”。不需要解决所有问题,只需要在一个点上做得比现有方案更好,并且能清晰地证明这一点。


误区警示

错误认知 正确理解
“必须发明全新算法才能赢” 对已有算法的精巧组合和工程优化同样有价值
“代码写完就完了” 没有实验数据和文档的代码不是完整的贡献
“一个人闷头做” 和 mentor、社区交流能避免方向错误
“越复杂越好” 评委更看重”简洁有效”而非”华而不实”
“最后一周冲刺” 前期稳步推进 + 充足的缓冲时间才是正道

← 第 12 章:技术挑战 目录