# Performance History (https://portunus.bybee.dev/en/docs/getting-started/performance-history)



This page preserves the baselines that precede the current
[v1.6.1 report](/en/docs/getting-started/performance). 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](/en/docs/getting-started/performance).

***

# v1.3.0 baseline (splice fast path introduction) [#v130-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) [#v130-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:

| Mode                                                       | Direct iperf3 loopback | Portunus uncapped | Portunus vs direct | Speedup vs v1.2.0 userspace |
| ---------------------------------------------------------- | ---------------------: | ----------------: | -----------------: | --------------------------: |
| splice OFF (`PORTUNUS_DISABLE_SPLICE=1`, v1.2.0 userspace) |          18,288 Mbit/s |      9,954 Mbit/s |              54.4% |                  (baseline) |
| splice ON (v1.3.0 default)                                 |          18,820 Mbit/s | **21,922 Mbit/s** |         **116.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) [#offered-load-sweep-v130-vs-v011-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 load | Direct | iptables REDIRECT | Portunus + splice OFF | Portunus + splice ON | splice speedup | splice OFF vs iptables | splice ON vs iptables |
| --------------: | -----: | ----------------: | --------------------: | -------------------: | -------------: | ---------------------: | --------------------: |
|      100 Mbit/s |    100 |               100 |                   100 |                  100 |          1.00× |                 100.0% |                100.0% |
|      500 Mbit/s |    500 |               500 |                   500 |                  500 |          1.00× |                 100.0% |                100.0% |
|        1 Gbit/s |  1,000 |             1,000 |                 1,000 |                1,000 |          1.00× |                 100.0% |                100.0% |
|      2.5 Gbit/s |  2,500 |             2,500 |                 2,500 |                2,498 |          1.00× |                 100.0% |                 99.9% |
|        5 Gbit/s |  4,999 |             5,005 |                 5,207 |                4,999 |          0.96× |                 104.0% |                 99.9% |
|      7.5 Gbit/s |  7,476 |             7,492 |                 6,775 |                7,499 |          1.11× |                  90.4% |                100.1% |
|       10 Gbit/s |  9,999 |            10,484 |                 8,870 |               10,035 |          1.13× |                  84.6% |                 95.7% |
| **12.5 Gbit/s** | 12,598 |            12,545 |             **8,556** |           **12,373** |      **1.45×** |              **68.2%** |             **98.6%** |
|   **15 Gbit/s** | 15,569 |            15,523 |             **9,953** |           **16,370** |      **1.64×** |              **64.1%** |            **105.5%** |
|   **18 Gbit/s** | 18,406 |            18,017 |             **8,347** |           **19,618** |      **2.35×** |              **46.3%** |            **108.9%** |
|   **20 Gbit/s** | 19,088 |            18,738 |             **8,690** |           **20,147** |      **2.32×** |              **46.4%** |            **107.5%** |

v1.3.0 splice ON reaches &#x2A;*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) [#v011-baseline-pre-splice]

## Linux iptables Baseline [#linux-iptables-baseline]

Captured on 2026-05-10 on:

```text
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:

```sh
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.

| Path                                    |           Throughput | vs direct | vs iptables | Retransmits |
| --------------------------------------- | -------------------: | --------: | ----------: | ----------: |
| Direct `iperf3` loopback                | 18,362-18,770 Mbit/s |   100.00% |     98-100% |           0 |
| `iptables` `nat/OUTPUT REDIRECT`        | 18,429-19,190 Mbit/s |  100-102% |     100.00% |           0 |
| `iperf3` through Portunus uncapped rule |   7,273-9,844 Mbit/s |    39-53% |      39-53% |        0-21 |
| Portunus capped at `1,048,576 B/s`      |         8.387 Mbit/s |       n/a |         n/a |           0 |

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 [#offered-load-sweep]

Command shape:

```sh
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 load |     Direct | iptables REDIRECT |  Portunus | Portunus vs iptables | Interpretation                                     |
| -----------: | ---------: | ----------------: | --------: | -------------------: | -------------------------------------------------- |
|   100 Mbit/s |    100.012 |           100.007 |    99.999 |               99.99% | No meaningful difference.                          |
|   500 Mbit/s |    499.986 |           500.011 |   500.138 |              100.03% | No meaningful difference.                          |
|     1 Gbit/s |    999.850 |           999.737 |   999.694 |              100.00% | No meaningful difference.                          |
|   2.5 Gbit/s |  2,499.613 |         2,499.853 | 2,499.678 |               99.99% | No meaningful difference.                          |
|     5 Gbit/s |  4,999.610 |         4,999.626 | 5,351.013 |              107.03% | At target; short-run pacing noise dominates.       |
|   7.5 Gbit/s |  7,471.440 |         7,499.448 | 8,172.746 |              108.98% | At target; short-run pacing noise dominates.       |
|    10 Gbit/s | 10,035.228 |         9,999.445 | 9,422.687 |               94.23% | Small visible gap, still close to target.          |
|  12.5 Gbit/s | 12,753.687 |        13,198.878 | 6,528.820 |               49.46% | Gap opens; Portunus is CPU/userspace-copy limited. |
|    15 Gbit/s | 15,482.215 |        16,335.550 | 7,836.651 |               47.97% | Clear gap.                                         |
|    18 Gbit/s | 18,228.201 |        17,948.877 | 7,919.914 |               44.12% | Clear gap.                                         |
|    20 Gbit/s | 18,600.141 |        18,265.199 | 6,105.970 |               33.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](/en/docs/getting-started/performance#offered-load-sweep)
confirms on the same host.
