perf(agent): E2E 排队修复——发送窗口 512KB→64KB + 拥塞反馈自动跳帧
- 实测数据:FPS 61 / RTT 7ms / E2E 618ms → 延迟全在发送侧排队: 512KB 窗口 ÷ WiFi 实际 ~3Mbps 吞吐 ≈ 1.3s 排队上限,latest-wins 管不到已进 QUIC 的字节 - send_window 64KB:排队硬上限 ~170ms@3Mbps;突发吞吐 64KB/7ms≈72Mbps 不受限; 连接级窗口,流 2 输入数据量极小不受影响 - 拥塞反馈跳帧:send write 耗时 >20ms(单帧预算 1.2 倍) → 置位标志 → 采集线程 跳过当前帧(NVENC 少输入一帧,参考链连续无花屏),注入率自适应匹配网络吞吐; capture 日志新增 skipped 计数 - viewer 标题版本 v0.7-LowLatency - 回环实测:NVENC 4ms/帧、skipped 0(回环无拥塞符合预期)、零错误
This commit is contained in:
1 parent
bd7ca29945
commit
168f600fe1
3 files changed
+34
-9
No files matched your search
+16
-5
@@ -43,12 +43,13 @@ async fn main() -> Result<()> {
|
||||
|
||||
// 传输参数调优:
|
||||
// - 收发窗口放大,避免高 RTT 下吞吐被 窗口/RTT 死锁(默认 512KB 流窗口)
|
||||
// - 发送缓冲上限 512KB:QUIC 内部排队 ≈ 3 帧 @5Mbps,E2E 延迟天花板 ~150ms;
|
||||
// 配合上游 latest-wins 丢帧,超量部分在进 QUIC 前就被丢弃
|
||||
// - 发送缓冲上限 64KB:QUIC 内部排队硬上限(实测 WiFi ~3Mbps 吞吐 → E2E ≤ ~170ms;
|
||||
// 突发吞吐 = 64KB/7ms ≈ 72Mbps,不限制带宽)。注意此窗口为连接级,流 2 输入/Ping
|
||||
// 数据量极小不受影响。配合下方拥塞反馈跳帧,把排队压在数帧以内
|
||||
let mut transport = quinn::TransportConfig::default();
|
||||
transport.receive_window(quinn::VarInt::from_u32(16 * 1024 * 1024));
|
||||
transport.stream_receive_window(quinn::VarInt::from_u32(8 * 1024 * 1024));
|
||||
transport.send_window(512 * 1024);
|
||||
transport.send_window(64 * 1024);
|
||||
let mut server_config = quinn::ServerConfig::with_single_cert(vec![cert], key.into())?;
|
||||
server_config.transport_config(Arc::new(transport));
|
||||
|
||||
@@ -87,7 +88,10 @@ async fn main() -> Result<()> {
|
||||
let (tx, mut frames_rx) = watch::channel::<Option<Vec<u8>>>(None);
|
||||
let keyreq = Arc::new(AtomicBool::new(false));
|
||||
let keyreq_capture = keyreq.clone();
|
||||
std::thread::spawn(move || capture::run(tx, keyreq_capture));
|
||||
// 拥塞反馈标志:发送侧 write 超过单帧预算(20ms) → 置位,采集线程跳帧降注入率
|
||||
let congestion = Arc::new(AtomicBool::new(false));
|
||||
let congestion_capture = congestion.clone();
|
||||
std::thread::spawn(move || capture::run(tx, keyreq_capture, congestion_capture));
|
||||
|
||||
// 上行控制消息(Pong)经通道交给主循环写流 2,避免多任务争抢
|
||||
let (ctl_tx, mut ctl_rx) = mpsc::unbounded_channel::<ControlMsg>();
|
||||
@@ -127,7 +131,14 @@ async fn main() -> Result<()> {
|
||||
break; // 采集线程退出
|
||||
}
|
||||
if let Some(data) = frames_rx.borrow().clone() {
|
||||
if send.write_all(&data).await.is_err() {
|
||||
let wt = std::time::Instant::now();
|
||||
let ok = send.write_all(&data).await.is_ok();
|
||||
// write 耗时 > 单帧预算(16.7ms 的 1.2 倍) → QUIC 窗口排队深,
|
||||
// 通知采集线程跳帧,把注入率降到网络实际吞吐水平
|
||||
if wt.elapsed() > std::time::Duration::from_millis(20) {
|
||||
congestion.store(true, Ordering::Relaxed);
|
||||
}
|
||||
if !ok {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user