mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 17:45:44 +00:00
perf(mpsc): batch flush for ring/UDP, per-packet flush for TCP
RingSink's poll_flush is a no-op (data already in ring buffer after start_send). Skip explicit poll_flush for ring/UDP tunnels to let poll_ready handle batching at max_buffer_count. FramedWriter (TCP) must flush per-packet: noop_waker can't wake the task when socket write returns Pending, so accumulated data in BufList would deadlock. Added direct_batch_flush flag to MpscTunnelSender, set based on tunnel_type at new_direct time: ring/udp: batch_flush = true (skip explicit flush) tcp: batch_flush = false (flush per packet) Benchmark (no hotpath, 3 runs avg): Ring: 1,124K → 1,120K pps (noise — flush was no-op anyway) TCP: 975K → 995K pps (+2%, within noise) UDP: 1,066K → 1,081K pps (+1.4%, within noise) Ring/UDP show no change because RingSink flush was already a no-op. TCP unchanged because it still flushes per-packet. The real writev batching opportunity for TCP would require async flush (not noop_waker), which is a separate optimization direction. All 210 peers tests pass. 6 netns tests fail (require root, unchanged).
This commit is contained in:
@@ -75,6 +75,7 @@ impl SpinSink {
|
|||||||
pub struct MpscTunnelSender {
|
pub struct MpscTunnelSender {
|
||||||
channel_tx: Option<Sender<ZCPacket>>,
|
channel_tx: Option<Sender<ZCPacket>>,
|
||||||
direct_sink: Option<Arc<SpinSink>>,
|
direct_sink: Option<Arc<SpinSink>>,
|
||||||
|
direct_batch_flush: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MpscTunnelSender {
|
impl MpscTunnelSender {
|
||||||
@@ -88,9 +89,13 @@ impl MpscTunnelSender {
|
|||||||
match guard.as_mut().poll_ready(&mut cx) {
|
match guard.as_mut().poll_ready(&mut cx) {
|
||||||
Poll::Ready(Ok(())) => {
|
Poll::Ready(Ok(())) => {
|
||||||
guard.as_mut().start_send(item)?;
|
guard.as_mut().start_send(item)?;
|
||||||
// poll_flush may return Pending when the consumer task hasn't
|
if self.direct_batch_flush {
|
||||||
// drained the ring yet. The data is already in the ring buffer
|
// RingSink: flush is no-op, data already in ring buffer.
|
||||||
// and will be consumed — treat Pending as success.
|
// Skip to allow poll_ready batching at max_buffer_count.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// FramedWriter (TCP): must flush per-packet, otherwise
|
||||||
|
// noop_waker can't wake when socket is full.
|
||||||
match guard.as_mut().poll_flush(&mut cx) {
|
match guard.as_mut().poll_flush(&mut cx) {
|
||||||
Poll::Ready(Err(e)) => return Err(e),
|
Poll::Ready(Err(e)) => return Err(e),
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
@@ -131,6 +136,7 @@ impl MpscTunnelSender {
|
|||||||
pub struct MpscTunnel<T> {
|
pub struct MpscTunnel<T> {
|
||||||
tx: Option<Sender<ZCPacket>>,
|
tx: Option<Sender<ZCPacket>>,
|
||||||
direct_sink: Option<Arc<SpinSink>>,
|
direct_sink: Option<Arc<SpinSink>>,
|
||||||
|
direct_batch_flush: bool,
|
||||||
|
|
||||||
tunnel: T,
|
tunnel: T,
|
||||||
stream: Option<Pin<Box<dyn ZCPacketStream>>>,
|
stream: Option<Pin<Box<dyn ZCPacketStream>>>,
|
||||||
@@ -158,6 +164,7 @@ impl<T: Tunnel> MpscTunnel<T> {
|
|||||||
Self {
|
Self {
|
||||||
tx: Some(tx),
|
tx: Some(tx),
|
||||||
direct_sink: None,
|
direct_sink: None,
|
||||||
|
direct_batch_flush: false,
|
||||||
tunnel,
|
tunnel,
|
||||||
stream: Some(stream),
|
stream: Some(stream),
|
||||||
task: Some(AbortOnDropHandle::new(task)),
|
task: Some(AbortOnDropHandle::new(task)),
|
||||||
@@ -166,9 +173,15 @@ impl<T: Tunnel> MpscTunnel<T> {
|
|||||||
|
|
||||||
pub fn new_direct(tunnel: T) -> Self {
|
pub fn new_direct(tunnel: T) -> Self {
|
||||||
let (stream, sink) = tunnel.split();
|
let (stream, sink) = tunnel.split();
|
||||||
|
let info = tunnel.info();
|
||||||
|
let batch_flush = info
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| matches!(i.tunnel_type.as_str(), "ring" | "udp"))
|
||||||
|
.unwrap_or(false);
|
||||||
Self {
|
Self {
|
||||||
tx: None,
|
tx: None,
|
||||||
direct_sink: Some(Arc::new(SpinSink::new(sink))),
|
direct_sink: Some(Arc::new(SpinSink::new(sink))),
|
||||||
|
direct_batch_flush: batch_flush,
|
||||||
tunnel,
|
tunnel,
|
||||||
stream: Some(stream),
|
stream: Some(stream),
|
||||||
task: None,
|
task: None,
|
||||||
@@ -239,6 +252,7 @@ impl<T: Tunnel> MpscTunnel<T> {
|
|||||||
MpscTunnelSender {
|
MpscTunnelSender {
|
||||||
channel_tx: self.tx.as_ref().cloned(),
|
channel_tx: self.tx.as_ref().cloned(),
|
||||||
direct_sink: self.direct_sink.clone(),
|
direct_sink: self.direct_sink.clone(),
|
||||||
|
direct_batch_flush: self.direct_batch_flush,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user