Skip to content

Performance Benchmark

Sweety vs Nginx across HTTP/1.1, HTTP/2, and HTTP/3 (QUIC).

ItemDetail
CPUIntel Celeron J4105 @ 1.50GHz (2 cores)
RAM1 GB
Link Bandwidth2.5 Gbps (TLS practical ceiling ~270 MB/s)
OSDebian Linux
TLSTLSv1.3, ECDSA P-256
Sweety0.2.0
Nginx1.29.7
Toolh2load · 15s per run

Idle memory: Sweety 8.65 MB vs Nginx 75.34 MB (−88%)


HTTP/1.1 (HTTPS TLSv1.3)

FileConnsServerRPSBW MB/sP50P95P99Mem MBCPU%Δ RPSNote
1 KB1000Sweety106,695134.391ms113ms137ms50.5100%+477%CPU bound
Nginx18,48023.9524ms564ms691ms134.4100%CPU bound
10 KB1000Sweety13,655137.3504ms1.78s2.89s50.842%+12%
Nginx12,187122.9602ms1.85s3.02s158.970%
100 KB1000Sweety1,520152.34.73s8.75s10.43s149.654%+9%
Nginx1,397139.65.13s9.13s10.92s246.042%
1 MB100Sweety236.6240.03.72s5.00s6.95s28.056%+22%Near BW limit
Nginx194.1197.54.14s6.92s8.13s140.030%
10 MB10Sweety26.53268.73.69s3.76s3.80s17.567%=🔗 BW ceiling
Nginx26.73271.13.64s3.87s4.30s89.640%🔗 BW ceiling

HTTP/2 (HTTPS TLSv1.3)

FileConnsServerRPSBW MB/sP50P95P99Mem MBCPU%Δ RPSNote
1 KB1000Sweety27,27627.9357ms374ms394ms75.9100%+48%CPU bound
Nginx18,47921.5508ms669ms853ms134.0100%CPU bound
10 KB1000Sweety14,148138.9462ms1.72s2.83s72.063%+8%
Nginx13,061130.1579ms1.68s2.77s158.075%
100 KB1000Sweety2,320236.83.33s5.29s6.90s340.5100%+799%Nginx 72% stalled¹
Nginx¹25827.71.50s2.57s2.67s250.735%Only 3864 reqs
1 MB100Sweety214.9251.13.70s5.88s8.63s428.145%+7%Near BW limit
Nginx201.8221.43.95s6.19s7.08s615.050%
10 MB10Sweety22.20265.43.76s4.20s4.23s64.542%−11%🔗 BW ceiling
Nginx24.93268.33.68s4.11s4.14s137.747%🔗 BW ceiling

¹ H2 100KB×1000: Nginx completed only 3,864 requests (258 req/s) vs Sweety's 34,797 (2,320 req/s). Nginx's lower P99 reflects fewer in-flight requests, not faster processing. ~72% of Nginx connections were stalled/queued.


HTTP/3 QUIC

FileConnsServerRPSBW MB/sP50P95P99Mem MBCPU%Δ RPSNote
1 KB1000Sweety33,10436.9100ms898ms2.30s427.2100%+115%CPU bound
Nginx15,41118.0170ms1.18s3.19s365.0100%CPU bound
10 KB1000Sweety14,638145.4139ms1.60s4.34s348.4100%+163%
Nginx5,56455.4335ms3.00s6.47s374.7100%
100 KB1000Sweety1,778181.61.53s8.15s12.61s625.2100%+143%
Nginx73373.53.31s8.94s10.42s908.0100%
1 MB100Sweety209.7217.11.08s1.58s1.95s204.3100%+206%
Nginx68.582.29.76s13.01s13.94s672.1100%
10 MB10Sweety20.27216.23.69s6.79s7.37s238.4100%+271%Near BW limit
Nginx5.4782.112.47s13.87s14.88s672.1100%

Analysis & Conclusions

Sweety Advantages

1. Small-file high-concurrency throughput dominance

H1 1KB: 107K vs 18K RPS (+477%), P99 only 137ms vs 691ms. H2 1KB also leads by 48%, P95–P99 spread just 374–394ms vs 669–853ms. Root cause: tokio async runtime scheduling overhead is far lower than Nginx's epoll + worker process model for massive short-lived requests.

2. HTTP/3 dominance across all file sizes

H3 leads by 115%–271% from 1KB to 10MB, with the gap widening as file size increases: 10MB Sweety 20.3 RPS / 216 MB/s vs Nginx 5.47 RPS / 82 MB/s (+271%). 1MB P99 only 1.95s vs Nginx 13.94s, memory only 204 MB vs 672 MB. Sweety's quinn/h3 QUIC implementation is far more efficient in UDP multiplexing, congestion control (BBR), and backpressure than Nginx's QUIC implementation. v0.2.0 introduces connection-level memory limiting (max_handlers), completely resolving OOM under high-concurrency large file transfers.

3. Memory efficiency

Idle footprint 8.65 MB vs 75.34 MB (−88%). Under load, 44–79% less memory in most scenarios. H3 1MB: 204 MB vs 672 MB (−70%), H2 1MB: 428 MB vs 615 MB (−30%).

4. Tail latency control

H1/H2 small-file scenarios show extremely tight P95–P99 spread with far lower stdev than Nginx. H3 1MB P99 only 1.95s (Nginx 13.94s), thanks to connection-level backpressure and global pread_stream semaphore dual control.

5. Zero errors

All test scenarios: zero request failures, zero timeouts. Nginx stalled 72% of connections in H2 100KB×1000 (only 3,864 requests completed vs Sweety's 34,797).

6. Protocol coverage

Single process serves H1 + H2 + H3 simultaneously, no extra compile-time modules needed. Nginx HTTP/3 requires recompilation and performs significantly worse.

Nginx Advantages

1. Ecosystem & production validation

20 years of production deployment, extensive documentation, mature third-party module ecosystem (WAF, Lua, OpenResty, etc.), global-scale operational experience and tooling. Vast community resources and professional support channels available when issues arise.

2. L4 proxy

Nginx stream {} module supports TCP/UDP L4 proxying for databases, SSH, and arbitrary TCP protocols. Sweety has not yet implemented this feature.

Sweety Immaturity

AreaStatusDetails
Production validation⚠️ Not production-testedNo long-term high-traffic real-world deployment; reliability, edge cases, memory leaks not fully validated
Module ecosystemBasic plugin systemOnly Rust trait registration; no Lua/WAF/OpenResty mature ecosystem
L4 proxy❌ Not implementedNo stream {}-style TCP/UDP passthrough
Conditional logic❌ No if / mapNo config-level conditional branching or variable mapping
Community sizeVery smallDocumentation, tutorials, third-party integrations all in early stages
Long-term stabilityUnknownNo months-long sustained high-load data; GC-free but Rust unsafe boundaries require ongoing audit

Use Case Comparison

ScenarioRecommendedRationale
API gateway / microservice ingressSweety5–6× RPS, P99 < 120ms, memory only ~50 MB
HTTP/3 deploymentSweety2–4× RPS across all sizes, Nginx QUIC clearly inferior
Edge nodes / embeddedSweety8.65 MB idle, single binary no deps, ideal for constrained environments
Small static sitesSweetyOne-line preset config, ACME auto-cert, works out of the box
Large CDN / file deliveryNginxBW-ceiling parity, more mature ops ecosystem
WAF / Lua extensions neededNginxOpenResty / ModSecurity mature security ecosystem
TCP/UDP L4 proxyNginxSweety does not yet support stream module
Critical production workloadsNginx20 years of production validation; Sweety is not yet production-proven

Test Environment Configuration

Sweety Configuration

See config/sweety.config.example

toml
# ═══════════════════════════════════════════════════════════════════
# Global Configuration
# ═══════════════════════════════════════════════════════════════════
[global]
worker_threads = 0
worker_connections = 65535
max_connections = 0
keepalive_timeout = 75
client_max_body_size = 0
client_header_buffer_size = 32
client_body_buffer_size = 512
gzip = false
log_level = "info"
h2_max_concurrent_streams = 128
h2_max_concurrent_reset_streams = 16384
h2_max_frame_size = 65535
h2_max_requests_per_conn = 10000
h2_max_pending_per_conn = 0

# ═══════════════════════════════════════════════════════════════════
# Site Configuration
# ═══════════════════════════════════════════════════════════════════
[[sites]]
name        = "benchmark"
server_name = ["172.19.1.5"]
listen      = [80]
listen_tls  = [443]
root        = "/www/wwwroot/local"
index       = ["index.html", "index.htm"]
force_https = false
gzip        = false

[sites.tls]
cert        = "/www/cert/cert.pem"
key         = "/www/cert/key.pem"
min_version = "tls1.2"
max_version = "tls1.3"
protocols   = ["h3", "h2", "http/1.1"]

[sites.tls.http3]
max_handlers                = 0
enable_0rtt                 = true
max_concurrent_bidi_streams = 2000
receive_window              = 16777216
stream_receive_window       = 4194304
send_window                 = 16777216

[[sites.locations]]
path    = "/"
handler = "static"

Nginx Configuration

nginx
# nginx.conf
daemon off;
master_process on;
user nginx;
worker_processes auto;
worker_rlimit_nofile 1048576;

events {
    use epoll;
    worker_connections 65535;
    multi_accept on;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    charset       off;
    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    aio threads;
    directio 8m;
    directio_alignment 4k;
    output_buffers 1 512k;
    keepalive_timeout 75;
    keepalive_requests 100000;
    reset_timedout_connection on;
    client_max_body_size 0;
    client_body_timeout 15s;
    send_timeout 300s;
    open_file_cache max=200000 inactive=120s;
    open_file_cache_valid 120s;
    open_file_cache_min_uses 1;
    open_file_cache_errors on;
    types_hash_max_size 4096;
    access_log off;
    log_not_found off;
    include /etc/nginx/conf.d/*.conf;
}
nginx
# site.conf
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;
    server_name 172.19.1.5;
    root /www/wwwroot/local;
    index index.html index.htm;
    ssl_certificate     /www/cert/cert.pem;
    ssl_certificate_key /www/cert/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_prefer_server_ciphers off;
    ssl_early_data on;
    add_header Alt-Svc 'h3=":443"; ma=86400' always;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~* \.bin$ {
        default_type application/octet-stream;
        try_files $uri =404;
    }
}

Reproducing These Results

  1. Prepare test files:
bash
dd if=/dev/urandom of=/www/wwwroot/local/1kb.bin bs=1K count=1
dd if=/dev/urandom of=/www/wwwroot/local/10kb.bin bs=10K count=1
dd if=/dev/urandom of=/www/wwwroot/local/100kb.bin bs=100K count=1
dd if=/dev/urandom of=/www/wwwroot/local/1mb.bin bs=1M count=1
dd if=/dev/urandom of=/www/wwwroot/local/10mb.bin bs=10M count=1
  1. Run benchmarks:
bash
# HTTP/1.1
h2load --duration=15 -c 1000 -m 10 -t 2 --h1 https://<host>/1kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 --h1 https://<host>/10kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 --h1 https://<host>/100kb.bin
h2load --duration=15 -c 100  -m 10 -t 2 --h1 https://<host>/1mb.bin
h2load --duration=15 -c 10   -m 10 -t 2 --h1 https://<host>/10mb.bin

# HTTP/2
h2load --duration=15 -c 1000 -m 10 -t 2 https://<host>/1kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 https://<host>/10kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 https://<host>/100kb.bin
h2load --duration=15 -c 100  -m 10 -t 2 https://<host>/1mb.bin
h2load --duration=15 -c 10   -m 10 -t 2 https://<host>/10mb.bin

# HTTP/3 (QUIC)
h2load --duration=15 -c 1000 -m 10 -t 2 --alpn-list=h3 https://<host>/1kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 --alpn-list=h3 https://<host>/10kb.bin
h2load --duration=15 -c 1000 -m 10 -t 2 --alpn-list=h3 https://<host>/100kb.bin
h2load --duration=15 -c 100  -m 10 -t 2 --alpn-list=h3 https://<host>/1mb.bin
h2load --duration=15 -c 10   -m 10 -t 2 --alpn-list=h3 https://<host>/10mb.bin
  1. Monitor resources during each test:
bash
# In a separate terminal
watch -n 0.5 'ps -o pid,rss,%cpu,comm -p $(pgrep sweety)'

Released under the Apache License 2.0