6 Commits

Author SHA1 Message Date
fanyang 90c45d2964 perf(mpsc): batch writev flush (threshold=8) for TCP — +7% pps
Add configurable batch flush threshold to SpinSink. When threshold > 1,
MpscTunnelSender::send accumulates packets in FramedWriter's BufList
without flushing. After N packets, poll_flush triggers a single writev()
syscall instead of N individual write() syscalls.

Implementation:
- SpinSink: pending_count + batch_threshold atomics
- MpscTunnelSender::send: flush every N packets via writev
- Default threshold=1 (per-packet flush, safe for handshake/control)
- Settable via set_batch_threshold() through PeerConn → Peer → PeerManager
- Bench: HOTPATH_BATCH env var, set after convergence

Batch threshold must be 1 during handshake (control packets are
request-response, can't be delayed). Bench sets threshold=8 only after
routes converge.

Benchmark (no hotpath, 3 runs avg):
  TCP batch=1:  985K pps
  TCP batch=8:  1,053K pps (+7%)
  Ring:         unchanged (flush is no-op for RingSink)
  UDP:          unchanged (flush is no-op for RingSink)

MpscTunnelSender::send avg: 343ns → 213ns (-38%, with hotpath) —
writev writes 8 Bytes in one syscall vs 8 write() calls.

All 210 peers tests pass. 6 netns tests fail (require root, unchanged).
2026-06-29 02:58:08 +08:00
fanyang d99efba64f perf(mpsc): extend noop_waker sync send to TCP tunnels
TCP tunnel uses FramedWriter (not RingSink), but start_send is still
sync (writes to BufList in memory). poll_flush does actual TCP write
syscall — noop_waker returns Ok for Pending (data stays in BufList,
flushed on next send when BufList >= 64).

Add TCP benchmark support via HOTPATH_TUNNEL=tcp. Note: TCP/UDP
convergence requires netns in bench environment (connector multi-bind
address behavior doesn't work for localhost without namespaces).

All 210 peers tests pass. Ring tunnel benchmark: 234K -> 508K pps (+117%).
2026-06-28 22:24:09 +08:00
fanyang 6d01908593 perf(mpsc): extend noop_waker sync send to UDP tunnels
UDP tunnel uses RingSink internally (same as ring tunnel). Extend
direct mode to include UDP. Fix poll_flush Pending to return Ok.

Add UDP benchmark support via HOTPATH_TUNNEL=udp env variable.

All 208 peers tests pass. Netns tests unchanged (require root).
2026-06-28 22:00:27 +08:00
fanyang 392a970db1 bench: add configurable pipeline depth via HOTPATH_PIPELINE env
FuturesUnordered-based pipeline to overlap encrypt with mpsc_send.
Tested depths 1/4/8/16: max +1.6% at depth=4, within noise. Pipeline
has limited value because try_send fast path eliminates await gaps
that would allow overlap. Default remains depth=1 (serial).
2026-06-28 18:55:36 +08:00
fanyang 37f742272b bench: support mimalloc/jemalloc allocator in cpu_hotspot_ring example
Add #[global_allocator] behind feature flags so the bench can test
different allocators. Previously the example used glibc malloc by
default (easytier-core.rs sets jemalloc/mimalloc only for the bin
target, not examples).

Benchmark (4 threads, 1400B, 15s, clone mode):
  glibc:    246K pps, 3.13us/pkt
  jemalloc: 246K pps, 3.21us/pkt
  mimalloc: 242K pps, 3.25us/pkt

All within noise. Single-threaded clone has low malloc contention;
~1500B small allocs are served efficiently by all tcaches.
2026-06-28 18:03:11 +08:00
fanyang 79035ea972 perf(hotpath): add cpu_hotspot_ring bench and send-chain optimization plan
- Add measure_all to PeerMap and CidrSet impl blocks (hotpath::measure_all)
- Add [profile.hotpath] for samply-compatible builds (strip=false, debug=line-tables-only)
- Add cpu_hotspot_ring example: 2-node ring tunnel with data-plane flooding (~234K pps)
- Add plans/006-send-chain-cpu-optimization.md based on hotpath+samply 423M sample analysis
  Key findings: dashmap redundancy (14.9%), metrics overhead (8.3%), mpsc (14.1%)
  Target: reduce send_msg_internal from 3.26us to ~2us per packet
2026-06-28 12:45:05 +08:00