From 1e3b63241356a3bfdc4c9eb24569198ff6d02cfd Mon Sep 17 00:00:00 2001 From: Datong Sun Date: Sun, 10 Apr 2022 06:14:30 -0700 Subject: [PATCH] docs(README) add benchmarking results based on `v0.3.2` --- README.md | 18 ++++++++++++------ .../phantun-vs-udp2raw-benchmark-result.png | Bin 0 -> 15584 bytes 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 images/phantun-vs-udp2raw-benchmark-result.png diff --git a/README.md b/README.md index 7b14540..cb62dac 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Table of Contents # Latest release -[v0.3.1](https://github.com/dndx/phantun/releases/tag/v0.3.1) +[v0.3.2](https://github.com/dndx/phantun/releases/tag/v0.3.2) # Overview @@ -59,6 +59,7 @@ Phantun is written in 100% safe Rust. It has been optimized extensively to scale systems and has no issue saturating all available CPU resources on a fast connection. See the [Performance](#performance) section for benchmarking results. +![Phantun benchmark results](images/phantun-vs-udp2raw-benchmark-result.png) ![Traffic flow diagram](images/traffic-flow.png) # Usage @@ -282,13 +283,18 @@ For users who wish to use `fake-tcp` library inside their own project, refer to Performance was tested on 2 AWS `t4g.xlarge` instances with 4 vCPUs and 5 Gb/s NIC over LAN. `nftables` was used to redirect UDP stream of `iperf3` to go through the Phantun/udp2raw tunnel between two test instances and MTU has been tuned to avoid fragmentation. +Phantun `v0.3.2` and `udp2raw_arm_asm_aes` `20200818.0` was used. These were the latest release of both projects as of Apr 2022. + Test command: `iperf3 -c -p -R -u -l 1400 -b 1000m -t 30 -P 5` -| Mode | Speed | Overall CPU Usage | -|---------------------------------------------------------------|----------------|--------------------------| -| Direct connection | 3.35 Gbits/sec | 25% (1 core at 100%) | -| Phantun | 2.03 Gbits/sec | 95% (all cores utilized) | -| udp2raw (cipher-mode=none auth-mode=none disable-anti-replay) | 876 Mbits/sec | 50% (2 cores at 100%) | +| Mode | Send Speed | Receive Speed | Overall CPU Usage | +|---------------------------------------------------------------------------------|----------------|----------------|-----------------------------------------------------| +| Direct (1 stream) | 3.00 Gbits/sec | 2.37 Gbits/sec | 25% (1 core at 100%) | +| Phantun (1 stream) | 1.30 Gbits/sec | 1.20 Gbits/sec | 60% (1 core at 100%, 3 cores at 50%) | +| udp2raw (`cipher-mode=none` `auth-mode=none` `disable-anti-replay`) (1 stream) | 1.30 Gbits/sec | 715 Mbits/sec | 40% (1 core at 100%, 1 core at 50%, 2 cores idling) | +| Direct connection (5 streams) | 5.00 Gbits/sec | 3.64 Gbits/sec | 25% (1 core at 100%) | +| Phantun (5 streams) | 5.00 Gbits/sec | 2.38 Gbits/sec | 95% (all cores utilized) | +| udp2raw (`cipher-mode=none` `auth-mode=none` `disable-anti-replay`) (5 streams) | 5.00 Gbits/sec | 770 Mbits/sec | 50% (2 cores at 100%) | [Back to TOC](#table-of-contents) diff --git a/images/phantun-vs-udp2raw-benchmark-result.png b/images/phantun-vs-udp2raw-benchmark-result.png new file mode 100644 index 0000000000000000000000000000000000000000..9f63541c46dece076d27213a34fe035cc1c8100a GIT binary patch literal 15584 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 literal 0 HcmV?d00001