From e18387b06b7cb0ef7b0faa1ef734a71d87a7c7ab Mon Sep 17 00:00:00 2001 From: fanyang Date: Sun, 28 Jun 2026 12:54:37 +0800 Subject: [PATCH] perf(mpsc): use try_send fast path to skip semaphore overhead MpscTunnelSender::send now tries try_send first, falling back to send().await only when the channel is full. try_send bypasses the tokio batch_semaphore Acquire::poll + add_permits_locked machinery (~9.4% of CPU in samply profiling), which is pure overhead when the channel has capacity. In the ring-tunnel bench (4 threads, 1400B, 15s) the channel(32) fast path hits >99%, so the fallback rarely triggers. Benchmark improvement: pps: 230K -> 246K (+7.0%) send_msg_internal avg: 3.25us -> 3.13us (-120ns/pkt) forward_one_round calls: 2.46M -> 706K (-71%, bigger batches) All mpsc tests pass. --- easytier/src/tunnel/mpsc.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/easytier/src/tunnel/mpsc.rs b/easytier/src/tunnel/mpsc.rs index fb4f68c9..32c2dcfa 100644 --- a/easytier/src/tunnel/mpsc.rs +++ b/easytier/src/tunnel/mpsc.rs @@ -20,8 +20,14 @@ pub struct MpscTunnelSender(Sender); impl MpscTunnelSender { pub async fn send(&self, item: ZCPacket) -> Result<(), TunnelError> { - self.0.send(item).await.with_context(|| "send error")?; - Ok(()) + match self.0.try_send(item) { + Ok(()) => Ok(()), + Err(TrySendError::Full(item)) => { + self.0.send(item).await.with_context(|| "send error")?; + Ok(()) + } + Err(TrySendError::Closed(_)) => Err(TunnelError::Shutdown), + } } pub fn try_send(&self, item: ZCPacket) -> Result<(), TunnelError> {