Files
NE_YuR/network/tcpquiclab/QUIC_FIX_REPORT.md

5.3 KiB
Raw Blame History

QUIC 性能测试修复报告

问题描述

在 TCP 与 QUIC 性能对比实验中QUIC 程序存在严重的性能问题:

协议 吞吐量 (修复前)
TCP ~2200 MB/s
QUIC ~6 MB/s

QUIC 吞吐量仅为 TCP 的 0.3%,这明显不正常。

问题分析

问题 1超时处理导致性能低下

原代码 (quic_perf_client.c 和 quic_perf_server.c)

int64_t timeout_ns = quiche_conn_timeout_as_nanos(conn);
if (timeout_ns > 0 && !has_outgoing && !finished_sending) {
    struct timespec ts;
    ts.tv_sec = timeout_ns / 1000000000;
    ts.tv_nsec = timeout_ns % 1000000000;
    nanosleep(&ts, NULL);
}

问题: quiche_conn_timeout_as_nanos() 返回的是连接空闲超时时间(配置为 10 秒),而不是下一次需要处理事件的时间。这导致程序在没有数据发送时会等待很长时间,严重降低了事件循环的响应速度。

问题 2客户端过早关闭连接

原代码中客户端发送完数据后只是简单地 usleep(1000) 然后继续循环,没有正确处理连接关闭逻辑。

修复方案

修复 1优化轮询间隔

quic_perf_server.c (第 186-191 行)

quiche_conn_on_timeout(conn);

// 使用更短的轮询间隔以提高响应速度
if (!has_outgoing) {
    usleep(100);  // 100微秒轮询
}

quic_perf_client.c (第 125-130 行)

quiche_conn_on_timeout(conn);

// 使用更短的轮询间隔以提高响应速度
if (!has_outgoing && !finished_sending) {
    usleep(100);  // 100微秒轮询
}

修复 2简化客户端发送完成后的处理

if (finished_sending && !has_outgoing) {
    usleep(1000);
}

修复结果

协议 修复前 修复后 提升
TCP ~2200 MB/s ~1400 MB/s -
QUIC ~6 MB/s ~92 MB/s 15倍

测试命令

正常网络环境测试

# TCP 测试
./tcp_perf_server &
sleep 1
./tcp_perf_client
wait

# QUIC 测试
./quic_perf_server &
sleep 1
./quic_perf_client
sleep 3

丢包环境测试 (5%)

# 添加 5% 丢包
sudo tc qdisc add dev lo root netem loss 5%

# 运行 TCP 和 QUIC 测试...

# 清除规则
sudo tc qdisc del dev lo root

延迟环境测试 (100ms)

# 添加 100ms 延迟
sudo tc qdisc add dev lo root netem delay 100ms

# 运行 TCP 和 QUIC 测试...

# 清除规则
sudo tc qdisc del dev lo root

检查和清除 tc 规则

# 查看当前规则
tc qdisc show dev lo

# 清除所有规则
sudo tc qdisc del dev lo root

为什么 QUIC 吞吐量仍然远小于 TCP

修复后 QUIC 达到 ~92 MB/s而 TCP 达到 ~1400 MB/sQUIC 仍然只有 TCP 的 6.5%。这是正常现象,原因如下:

1. 协议栈实现位置不同

协议 实现位置 特点
TCP 内核空间 高度优化,零拷贝,硬件卸载
QUIC 用户空间 需要系统调用,数据拷贝开销大

TCP 在 Linux 内核中经过 30+ 年优化,支持:

  • 零拷贝 (Zero-copy)sendfile(), splice()
  • 硬件卸载 (TSO/GSO):网卡直接分段
  • 内核旁路优化:减少上下文切换

2. 加密开销

QUIC 强制加密所有数据(使用 TLS 1.3),而本实验中的 TCP 是明文传输:

QUIC 数据路径: 应用数据 → TLS加密 → UDP封装 → 发送
TCP 数据路径:  应用数据 → TCP封装 → 发送

加密/解密操作消耗大量 CPU 资源。

3. UDP vs TCP 的系统调用效率

操作 TCP QUIC (UDP)
发送 100MB 少量大块 write() 大量小包 sendto()
系统调用次数

TCP 可以一次 write() 发送大块数据内核自动分段。QUIC 基于 UDP每个数据包都需要单独的 sendto() 调用。

4. 数据包大小限制

#define MAX_DATAGRAM_SIZE 1350  // QUIC 单包最大载荷

QUIC 受 UDP MTU 限制,每个包最多 ~1350 字节。发送 100MB 需要约 77,000+ 个数据包

5. 本地回环测试的特殊性

localhost 测试中网络延迟几乎为零TCP 的内核优化优势被放大。QUIC 的优势(如 0-RTT、连接迁移、多路复用在这种环境下无法体现。

6. QUIC 真正的优势场景

QUIC 设计目标不是追求极限吞吐量,而是在真实网络环境中提供更好的用户体验:

场景 QUIC 优势
高延迟网络 0-RTT 快速连接建立
丢包环境 无队头阻塞,单流丢包不影响其他流
移动网络 连接迁移,切换 WiFi/4G 不断连
多路复用 单连接多流,避免 TCP 队头阻塞

从你的日志可以看到,在 100ms 延迟环境下:

  • TCP: 0.98 MB/s
  • QUIC: 2.41 MB/s

QUIC 在高延迟环境下性能是 TCP 的 2.5 倍!


总结

环境 TCP QUIC 胜者
本地回环 (无延迟) ~1400 MB/s ~92 MB/s TCP
100ms 延迟 0.98 MB/s 2.41 MB/s QUIC

结论: QUIC 在理想网络环境下吞吐量低于 TCP 是正常的这是用户空间实现、强制加密等设计权衡的结果。但在真实的高延迟、高丢包网络环境中QUIC 能够提供更好的性能和用户体验。