Skip to content

perf(windows): batch Wintun reads and RIO sends#138

Merged
encodeous merged 6 commits into
encodeous:mainfrom
tionis:windows-performance-1
Jul 25, 2026
Merged

perf(windows): batch Wintun reads and RIO sends#138
encodeous merged 6 commits into
encodeous:mainfrom
tionis:windows-performance-1

Conversation

@tionis

@tionis tionis commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Improve Windows data-plane performance by batching Wintun receives and RIO transmits.

The RIO path now reserves and submits an entire packet batch under one lock,
uses deferred sends followed by a single commit, and retains an immediate-send
fast path for single-packet traffic. The maximum Windows packet batch size is
increased from 16 to 32 based on throughput and latency testing.

Changes

  • Advertise and process bounded Wintun receive batches.
  • Drain immediately available Wintun packets into the caller-provided batch.
  • Drain RIO transmit completions once per batch.
  • Reserve transmit-ring capacity for the complete batch under one lock.
  • Submit multi-packet batches with RIO_MSG_DEFER and one
    RIO_MSG_COMMIT_ONLY request.
  • Send single-packet requests immediately to avoid an unnecessary commit call
    on latency-sensitive traffic.
  • Increase the Wintun and RIO batch limits from 16 to 32 packets.
  • Add tests covering:
    • transmit-ring capacity and full-state handling;
    • counter wraparound;
    • failed-send rollback;
    • immediate versus deferred send flags.

Capacity is reserved before submitting deferred requests. This avoids waiting
for completions from requests that Windows has not yet committed.

Performance

Tests ran from a Windows 11 Pro system with an Intel i7-6700K to a Linux Nylon
endpoint over a gigabit Ethernet switch and a direct Nylon link.

A bounded A/B/A comparison of the final batch-size adjustment produced:

Forward workload Batch 16 A1 Batch 32 Batch 16 A2
TCP, 1 stream 859.24 Mbit/s 853.90 Mbit/s 817.72 Mbit/s
TCP, 4 streams 769.53 Mbit/s 884.53 Mbit/s 850.59 Mbit/s
UDP, 1372-byte payload 729.23 Mbit/s 803.35 Mbit/s 898.18 Mbit/s
UDP, 64-byte payload 33.84 Mbit/s 58.29 Mbit/s 34.68 Mbit/s
UDP, 64-byte loss 65.90% 41.68% 65.18%

For the 64-byte workload, batch 32 delivered approximately 70% more traffic
than the mean of the controls, reduced loss by 23.9 percentage points, and used
approximately 27% less Nylon CPU time per delivered bit.

TCP and normal-packet UDP remained within the variability of the bracketing
controls. Batch sizes of 8 and 64 were also tested: 8 was slower, while 64
materially regressed normal-packet UDP throughput.

Latency

An A/B/A test using a temporary UDP echo endpoint found no measurable latency
regression from increasing the batch size:

Scenario Metric Batch 16 A1 Batch 32 Batch 16 A2
Idle p50 RTT 1.0150 ms 0.6506 ms 0.8555 ms
Idle p95 RTT 1.6306 ms 1.1083 ms 1.3170 ms
Idle p99 RTT 2.2454 ms 2.6347 ms 2.8297 ms
TCP load p95 RTT 47.4889 ms 48.0725 ms 49.1433 ms
TCP load p99 RTT 53.6151 ms 54.1835 ms 55.4501 ms

The single-packet RIO fast path also preserved batching throughput while
removing the extra defer/commit operation from interactive traffic.

Profiling

A symbolized 20-second CPU profile during bounded 64-byte UDP traffic showed:

  • runtime.cgocall: 85.86% of sampled CPU time;
  • RIOSendEx: approximately 40% of cumulative samples;
  • traffic-control batching: 2.30%;
  • ChaCha sealing: less than 1%.

This indicates that the remaining Windows cost is predominantly at the Windows
API boundary rather than in Nylon traffic control or encryption.

Validation

  • Full go test ./... suite passes.
  • Complete Windows executables cross-compile for amd64 and arm64.
  • The Windows connection tests pass natively on Windows.
  • The native tests cover ring capacity, counter wraparound, full-state
    handling, failed-send rollback, and immediate/deferred send selection.

Copilot AI review requested due to automatic review settings July 24, 2026 12:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the Windows data-plane by enabling packet batching in the Wintun receive path and batching RIO sends under fewer locks/commits, with a new 32-packet batch limit intended to improve throughput (especially for small packets) without regressing latency.

Changes:

  • Implement bounded Wintun receive batching and advertise a Windows TUN batch size of 32.
  • Batch WinRIO transmits: reserve ring capacity for the whole batch, defer multi-packet sends, and commit once per batch while keeping a single-packet fast path.
  • Add Windows-specific unit tests for RIO send flag selection and ring-buffer accounting helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
polyamide/tun/tun_windows.go Introduces Wintun receive batching and increases the advertised TUN batch size to 32.
polyamide/conn/bind_windows.go Adds ring capacity helpers and refactors WinRIO send to support deferred multi-packet batches with a single commit.
polyamide/conn/bind_windows_test.go Adds unit tests for rioSendFlags and ring-buffer helper methods used by the new batching logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread polyamide/tun/tun_windows.go
Comment thread polyamide/conn/bind_windows.go
@tionis

tionis commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

The failed e2e test just seems to be test flakiness

@encodeous

Copy link
Copy Markdown
Owner

This change looks good, thank you for contributing!

You should also consider submitting this patch to upstream wireguard-go!

@encodeous
encodeous merged commit 428e555 into encodeous:main Jul 25, 2026
5 of 6 checks passed
@tionis

tionis commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Hm true, I could look into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants