Files
NE_YuR/network/tcpquiclab/QUIC_FIX_REPORT.md

206 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)**
```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 行)**
```c
quiche_conn_on_timeout(conn);
// 使用更短的轮询间隔以提高响应速度
if (!has_outgoing) {
usleep(100); // 100微秒轮询
}
```
**quic_perf_client.c (第 125-130 行)**
```c
quiche_conn_on_timeout(conn);
// 使用更短的轮询间隔以提高响应速度
if (!has_outgoing && !finished_sending) {
usleep(100); // 100微秒轮询
}
```
### 修复 2简化客户端发送完成后的处理
```c
if (finished_sending && !has_outgoing) {
usleep(1000);
}
```
## 修复结果
| 协议 | 修复前 | 修复后 | 提升 |
|------|--------|--------|------|
| TCP | ~2200 MB/s | ~1400 MB/s | - |
| QUIC | ~6 MB/s | ~92 MB/s | **15倍** |
## 测试命令
### 正常网络环境测试
```bash
# TCP 测试
./tcp_perf_server &
sleep 1
./tcp_perf_client
wait
# QUIC 测试
./quic_perf_server &
sleep 1
./quic_perf_client
sleep 3
```
### 丢包环境测试 (5%)
```bash
# 添加 5% 丢包
sudo tc qdisc add dev lo root netem loss 5%
# 运行 TCP 和 QUIC 测试...
# 清除规则
sudo tc qdisc del dev lo root
```
### 延迟环境测试 (100ms)
```bash
# 添加 100ms 延迟
sudo tc qdisc add dev lo root netem delay 100ms
# 运行 TCP 和 QUIC 测试...
# 清除规则
sudo tc qdisc del dev lo root
```
### 检查和清除 tc 规则
```bash
# 查看当前规则
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. 数据包大小限制
```c
#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 能够提供更好的性能和用户体验。