Portunus

Performance History

Pre-v1.6.1 baselines — the v1.3.0 splice-introduction tables and the v0.11 pre-splice iptables comparison, preserved verbatim for traceability.

This page preserves the baselines that precede the current v1.6.1 report. It is kept verbatim so each successive report remains reproducible against a fixed prior reference — there is no operational reason to consult it during day-to-day use of Portunus.

  • v1.3.0 introduced the Linux TCP splice fast path and is the first release to close the gap to iptables above 12.5 Gbit/s. Measured on the original Debian 13 host.
  • v0.11 is the last pre-splice baseline. The v1.3.0 numbers were measured against it.

The current head-to-head (re-measured on the same host, plus a portunus-standalone vs portunus-client comparison) is the v1.6.1 report.


v1.3.0 baseline (splice fast path introduction)

Captured on the original dedicated Linux bench host (Debian 13, kernel 6.12.74-2, glibc 2.41 — same hardware as the v0.11 iptables baseline below).

v1.3.0 Headline (Linux TCP splice fast path)

The v1.3.0 splice fast path more than doubles single-flow uncapped throughput on plain TCP rules with no per-rule and no per-owner bandwidth cap:

ModeDirect iperf3 loopbackPortunus uncappedPortunus vs directSpeedup vs v1.2.0 userspace
splice OFF (PORTUNUS_DISABLE_SPLICE=1, v1.2.0 userspace)18,288 Mbit/s9,954 Mbit/s54.4%(baseline)
splice ON (v1.3.0 default)18,820 Mbit/s21,922 Mbit/s116.5%2.20×

The splice ON case exceeds the direct iperf3 loopback baseline because the kernel-to-kernel splice + pipe path avoids the userspace read → memcpy → write round-trip that bounds every userspace TCP application on this host — including iperf3 itself.

Offered-load sweep (v1.3.0 vs v0.11 saturation point)

The v0.11 report identified ≥ 12.5 Gbit/s as the offered-load band where Portunus saturates early and falls well behind iptables. The v1.3.0 splice fast path closes that gap on this host. Same 11-point sweep, same iptables REDIRECT comparison, same host — captured under v1.3.0 with splice ON (default) and splice OFF (PORTUNUS_DISABLE_SPLICE=1) back-to-back:

Offered loadDirectiptables REDIRECTPortunus + splice OFFPortunus + splice ONsplice speedupsplice OFF vs iptablessplice ON vs iptables
100 Mbit/s1001001001001.00×100.0%100.0%
500 Mbit/s5005005005001.00×100.0%100.0%
1 Gbit/s1,0001,0001,0001,0001.00×100.0%100.0%
2.5 Gbit/s2,5002,5002,5002,4981.00×100.0%99.9%
5 Gbit/s4,9995,0055,2074,9990.96×104.0%99.9%
7.5 Gbit/s7,4767,4926,7757,4991.11×90.4%100.1%
10 Gbit/s9,99910,4848,87010,0351.13×84.6%95.7%
12.5 Gbit/s12,59812,5458,55612,3731.45×68.2%98.6%
15 Gbit/s15,56915,5239,95316,3701.64×64.1%105.5%
18 Gbit/s18,40618,0178,34719,6182.35×46.3%108.9%
20 Gbit/s19,08818,7388,69020,1472.32×46.4%107.5%

v1.3.0 splice ON reaches 12,373 Mbit/s (98.6% of iptables) at 12.5 Gbit/s offered, versus the v0.11 6,529 Mbit/s (49% of iptables). Through the 15-20 Gbit/s band the splice ON path stays within iperf3 short-run noise of both the direct loopback and the iptables REDIRECT baseline, while the splice OFF userspace path saturates around the v1.2.0 userspace-memcpy ceiling of ~8.5-10 Gbit/s.


v0.11 baseline (pre-splice)

Linux iptables Baseline

Captured on 2026-05-10 on:

Linux host 6.12.74+deb13+1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.74-2 (2026-03-08) x86_64
rustc 1.95.0 (59807616e 2026-04-14)
iperf 3.18

Command:

TMPDIR=/root scripts/perf_loopback.py --seconds 10 --omit-seconds 2 \
  --with-iptables --iptables-bin /usr/sbin/iptables

TMPDIR=/root was required on this host because /tmp is tmpfs, and portunus-server intentionally refuses tmpfs-backed TLS / persistence state. The iptables rule was cleaned up after the run; iptables -t nat -S OUTPUT returned only the default -P OUTPUT ACCEPT policy.

PathThroughputvs directvs iptablesRetransmits
Direct iperf3 loopback18,362-18,770 Mbit/s100.00%98-100%0
iptables nat/OUTPUT REDIRECT18,429-19,190 Mbit/s100-102%100.00%0
iperf3 through Portunus uncapped rule7,273-9,844 Mbit/s39-53%39-53%0-21
Portunus capped at 1,048,576 B/s8.387 Mbit/sn/an/a0

The uncapped maximum is intentionally reported as a range: at this speed the measurement is dominated by scheduler noise, TCP pacing, and userspace-copy CPU placement.

Offered-Load Sweep

Command shape:

TMPDIR=/root scripts/perf_loopback.py --seconds 5 --omit-seconds 1 \
  --with-iptables --offered-mbps 100,500,1000,2500,5000,7500,10000,12500,15000,18000,20000 \
  --iptables-bin /usr/sbin/iptables
Offered loadDirectiptables REDIRECTPortunusPortunus vs iptablesInterpretation
100 Mbit/s100.012100.00799.99999.99%No meaningful difference.
500 Mbit/s499.986500.011500.138100.03%No meaningful difference.
1 Gbit/s999.850999.737999.694100.00%No meaningful difference.
2.5 Gbit/s2,499.6132,499.8532,499.67899.99%No meaningful difference.
5 Gbit/s4,999.6104,999.6265,351.013107.03%At target; short-run pacing noise dominates.
7.5 Gbit/s7,471.4407,499.4488,172.746108.98%At target; short-run pacing noise dominates.
10 Gbit/s10,035.2289,999.4459,422.68794.23%Small visible gap, still close to target.
12.5 Gbit/s12,753.68713,198.8786,528.82049.46%Gap opens; Portunus is CPU/userspace-copy limited.
15 Gbit/s15,482.21516,335.5507,836.65147.97%Clear gap.
18 Gbit/s18,228.20117,948.8777,919.91444.12%Clear gap.
20 Gbit/s18,600.14118,265.1996,105.97033.43%Beyond this host's Portunus comfortable range.

Above 12.5 Gbit/s offered, iptables stays near the offered load while the pre-splice Portunus userspace path saturates much earlier. This is the band the v1.3.0 splice fast path closed (see above), and that the v1.6.1 report confirms on the same host.

On this page