Source: https://superweb.to/benchmarks/games-report.html

WSS / AWS / OCTOBER 17, 2026

# Game traffic, measured.

## Result

At 64 simultaneous simulated WSS players sending 64-byte messages at 60 Hz, the new build connected 64/64 and delivered 3,840 verified messages/s. The old build connected 2/64 and delivered a median 120/s. This is a concurrency improvement; the old build’s latency describes only the few sessions that connected.

## Method

Three sequential rounds per build, eight seconds of traffic per normal case, with a separate c7g.xlarge load generator in us-east-2a. The primary server is a c7g.large (2 vCPU, 4 GiB); the small-host check is a fresh t4g.nano (2 vCPU, 0.5 GiB, Unlimited CPU credits). Both use two Superweb workers, Ubuntu 24.04 ARM64, identical payloads, no WebSocket compression, verified TLS, and the same Python 3.12 / websockets 17.1 backend on loopback. The backend echoes or broadcasts binary payloads; no physics, database or rendering work is included. Server processes are reused across cases and restarted between builds. The before build was measured first; this was not a randomized trial. Nano results are short burst measurements, not a sustained capacity guarantee.

## How to read the tables

All attempted connections start together with a three-second handshake deadline. Message latency is measured from send to receipt on one client clock and includes client scheduling, TLS, network, proxy and backend. Tables show medians of per-run rates and p99s, not a pooled percentile. Failed connections and unanswered messages remain in the error totals. Broadcast counts recipient deliveries. Eight paused flood readers do not send game messages; the other 56 target 3,360/s. Idle tests check protocol ping/pong; reconnect tests verify one message per successful connection. Neither has a game-message latency sample. CPU and memory are sampled every 0.2 seconds. Server CPU 100% means one full core; these hosts have two cores, so their process ceiling is 200%. CPU averages include setup, traffic and teardown. Server RAM used is the maximum sampled PSS across the displayed rounds, with shared pages apportioned; it excludes the backend and OS. Raw data also includes RSS, backend PSS, host used memory (total minus available), client CPU and scheduling lag. Short samples can miss allocation peaks. Lower baseline CPU and latency reflect its many failed connections, not equal completed work.

## Primary server · c7g.large

| Workload | Build | Connected / attempted per round | Messages/s · median | p99 ms · median | Errors · all rounds | Server CPU % · median | Server RAM used · peak MiB PSS |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 player · 64 B · 60 Hz | before | 1/1 | 60 | 0.48 | 0 | 0.2 | 14.7 |
| 64 players · 64 B · 60 Hz | before | 2/64 | 120 | 0.51 | 186 | 0.3 | 14.7 |
| 64 players · 1024 B · 20 Hz | before | 1–2/64 | 20 | 0.57 | 188 | 0.7 | 14.7 |
| 64 players · rooms of 16 · 20 Hz | before | 1–2/64 | 20 | 0.57 | 188 | 0.2 | 14.7 |
| 64 players + 1 MiB assets · 60 Hz | before | 1–2/64 | 120 | 0.59 | 203 | 1.1 | 14.7 |
| 16 idle sessions · 12 s + heartbeats | before | 1–2/16 | 0 | — | 43 | 0.0 | 14.7 |
| 56 players + 8 paused readers | before | 2/64 | 60 | 0.54 | 186 | 0.2 | 14.7 |
| 5 waves × 64 reconnects | before | 5–6/320 | — | — | 944 | 3.1 | 14.8 |
| 1 player · 64 B · 60 Hz | after | 1/1 | 60 | 0.48 | 0 | 0.1 | 10.3 |
| 64 players · 64 B · 60 Hz | after | 64/64 | 3,840 | 4.23 | 0 | 11.6 | 10.3 |
| 64 players · 1024 B · 20 Hz | after | 64/64 | 1,280 | 4.97 | 0 | 4.5 | 10.5 |
| 64 players · rooms of 16 · 20 Hz | after | 64/64 | 20,480 | 28.23 | 0 | 29.7 | 10.5 |
| 64 players + 1 MiB assets · 60 Hz | after | 64/64 | 3,840 | 4.64 | 0 | 16.3 | 10.5 |
| 16 idle sessions · 12 s + heartbeats | after | 16/16 | 0 | — | 0 | 0.1 | 10.5 |
| 56 players + 8 paused readers | after | 64/64 | 3,360 | 3.88 | 0 | 12.8 | 10.5 |
| 5 waves × 64 reconnects | after | 320/320 | — | — | 0 | 83.4 | 10.5 |

## Small host · t4g.nano

| Workload | Build | Connected / attempted per round | Messages/s · median | p99 ms · median | Errors · all rounds | Server CPU % · median | Server RAM used · peak MiB PSS |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 64 players · 64 B · 60 Hz | before | 1–2/64 | 120 | 0.79 | 187 | 2.6 | 15.6 |
| 64 players + 1 MiB assets · 60 Hz | before | 1–2/64 | 120 | 1.10 | 203 | 1.5 | 15.6 |
| 56 players + 8 paused readers | before | 1–2/64 | 60 | 0.84 | 187 | 2.2 | 15.6 |
| 5 waves × 64 reconnects | before | 5–10/320 | — | — | 940 | 8.1 | 15.8 |
| 64 players · 64 B · 60 Hz | after | 64/64 | 3,840 | 4.42 | 0 | 21.9 | 11.8 |
| 64 players + 1 MiB assets · 60 Hz | after | 64/64 | 3,840 | 6.41 | 0 | 29.7 | 12.0 |
| 56 players + 8 paused readers | after | 64/64 | 3,360 | 3.54 | 0 | 22.4 | 12.1 |
| 5 waves × 64 reconnects | after | 320/320 | — | — | 0 | 142.0 | 12.7 |

## Five-minute sustained run

The new build sustained 256/256 WSS sessions for 300 seconds: 1,536,000 verified echoes, 5,120 messages/s, 18.57 ms p99, 0 recorded errors. This is an observed workload, not a maximum-player claim.

| Workload | Build | Connected / attempted per round | Messages/s · median | p99 ms · median | Errors · all rounds | Server CPU % · median | Server RAM used · peak MiB PSS |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 256 players · 256 B · 20 Hz · 5 min | after | 256/256 | 5,120 | 18.57 | 0 | 17.8 | 21.1 |

## Asset downloads during play

Each request opens a new verified HTTPS connection. The client waits 50 ms after each completion; this measures coexistence with game traffic, not maximum asset throughput.

| Host/build | Verified 1 MiB assets · all rounds | Rounds with completed assets | Asset p99 ms · median | Asset failures · all rounds |
| --- | --- | --- | --- | --- |
| server-before | 145 | 1/3 | 5.85 | 16 |
| server-after | 414 | 3/3 | 11.10 | 0 |
| nano-before | 136 | 1/3 | 9.95 | 16 |
| nano-after | 387 | 3/3 | 14.59 | 0 |

## Backend, client and host resources

| Host/build | Workload | Server CPU % | Backend CPU % | Client CPU % | Backend peak PSS MiB | Host peak used MiB | Client schedule lag p99 ms |
| --- | --- | --- | --- | --- | --- | --- | --- |
| server-before | 64 players · 64 B · 60 Hz | 0.3 | 0.6 | 1.9 | 19.5 | 459.2 | 1.16 |
| server-before | 64 players · rooms of 16 · 20 Hz | 0.2 | 0.4 | 1.1 | 19.5 | 455.7 | 1.08 |
| server-before | 64 players + 1 MiB assets · 60 Hz | 1.1 | 0.7 | 2.2 | 19.5 | 459.8 | 1.18 |
| server-before | 56 players + 8 paused readers | 0.2 | 0.7 | 1.6 | 19.8 | 455.6 | 1.07 |
| server-after | 64 players · 64 B · 60 Hz | 11.6 | 16.8 | 29.6 | 21.9 | 457.1 | 3.96 |
| server-after | 64 players · rooms of 16 · 20 Hz | 29.7 | 44.1 | 46.6 | 22.0 | 451.9 | 4.68 |
| server-after | 64 players + 1 MiB assets · 60 Hz | 16.3 | 17.4 | 37.3 | 22.0 | 452.1 | 4.20 |
| server-after | 56 players + 8 paused readers | 12.8 | 15.9 | 30.5 | 23.2 | 509.3 | 4.39 |
| nano-before | 64 players · 64 B · 60 Hz | 2.6 | 1.1 | 1.9 | 21.2 | 230.0 | 1.17 |
| nano-before | 64 players + 1 MiB assets · 60 Hz | 1.5 | 1.2 | 2.2 | 21.2 | 229.6 | 1.18 |
| nano-before | 56 players + 8 paused readers | 2.2 | 1.1 | 1.8 | 21.2 | 233.1 | 1.07 |
| nano-after | 64 players · 64 B · 60 Hz | 21.9 | 25.9 | 30.8 | 24.0 | 234.2 | 3.91 |
| nano-after | 64 players + 1 MiB assets · 60 Hz | 29.7 | 26.1 | 37.7 | 24.0 | 230.6 | 4.05 |
| nano-after | 56 players + 8 paused readers | 22.4 | 24.1 | 30.8 | 24.3 | 256.1 | 3.63 |
| server-sustained | 256 players · 256 B · 20 Hz · 5 min | 17.8 | 23.1 | 43.8 | 24.8 | 476.8 | 12.76 |

## What changed

The new path connects, upgrades and relays WebSockets through epoll/kqueue without occupying a worker per session. Lazy queues cap application buffering at 16 KiB per direction by default, then pause the source. TLS and kernel buffers are additional. Write and handshake deadlines close stalled sessions. WebSockets, HTTP/1 asset sends and HTTP/2 pumps yield between bounded turns. HTTP request buffers are freed after upgrade, and socket buffers use kernel defaults. SIGTERM stops accepts and drains active work for a configurable interval. At the time of these measurements, ordinary HTTP reverse-proxy requests still used the blocking path. A subsequent HTTP streaming update removes that worker interference; these game-load measurements have not been rerun for it.

## Assembly and profiling

The ARM64 NEON header comparator processes eight bounded bytes at a time. On the benchmark ARM host, Connection, Sec-WebSocket-Accept and X-Forwarded-Proto comparisons were 3.245×, 3.575× and 5.076× faster than the scalar microbenchmark. Seven-byte Upgrade was effectively tied (0.998×), so short names retain the C path. These are comparison-kernel results, not game-throughput gains. The existing NEON/AVX2 header scanner is reused. A separate 256-connection perf run captured 658 cycle samples with zero lost samples. The largest individual symbol was the kernel syscall entry (6.08%); the profile also includes TCP and TLS work. The header comparator is not a dominant steady-state tunnel cost. This profile is diagnostic, with its overhead excluded from the timing tables.

## Limits

These workloads do not establish maximum server capacity or a multiplayer ranking against nginx, Caddy and lighttpd. The Python backend and client can become bottlenecks; inspect their CPU and scheduling lag in the resource table. The sustained test uses 256 connections at a fixed rate, not a saturation search. t4g.nano CPU and network bursting make short results unsuitable for extrapolation to an all-day player count. The nano’s final ENA counters reported 128 ingress and 354 egress bandwidth-allowance-exceeded events. These are cumulative since boot, including setup; no per-case snapshots were taken, so they cannot be attributed to an individual row. The historical static-file results remain a separate dataset.

## Builds and reproduction

Before: commit 44d8007, Linux ARM64 SHA-256 54b64d83a0493f99c5c26f4a166402708982fb1882329cc07b76ae2aa910d03d. After: Linux ARM64 SHA-256 58fce16b5d6c28a95390ea883cdc79ae846ebc79885c078641c6519f7e54f121. Both compiled with -O3 -flto -mcpu=neoverse-n1. The after binary was the Linux download and deployed server binary for the WebSocket release. Current downloads include a subsequent HTTP streaming update. The published historical static-file tests used their own identified binaries.

Normal and sanitizer suites passed on macOS ARM64 and Linux ARM64. Intel macOS tests passed with TLS cases skipped. The starter’s delta test and a two-browser-player check also passed.

[Reproduction instructions](https://superweb.to/benchmarks/games/README.txt) · [Traffic generator and backend](https://superweb.to/benchmarks/games/game_traffic.py) · [Resource sampler](https://superweb.to/benchmarks/games/game_monitor.py) · [Aggregation script](https://superweb.to/benchmarks/games/summarize_games.py)

## Retained measurements

- [summary.json](https://superweb.to/benchmarks/games/summary.json)
- [server-before.json](https://superweb.to/benchmarks/games/server-before.json)
- [server-after.json](https://superweb.to/benchmarks/games/server-after.json)
- [nano-before.json](https://superweb.to/benchmarks/games/nano-before.json)
- [nano-after.json](https://superweb.to/benchmarks/games/nano-after.json)
- [server-sustained.json](https://superweb.to/benchmarks/games/server-sustained.json)
- [server-before-resources.jsonl.gz](https://superweb.to/benchmarks/games/server-before-resources.jsonl.gz)
- [server-after-resources.jsonl.gz](https://superweb.to/benchmarks/games/server-after-resources.jsonl.gz)
- [nano-before-resources.jsonl.gz](https://superweb.to/benchmarks/games/nano-before-resources.jsonl.gz)
- [nano-after-resources.jsonl.gz](https://superweb.to/benchmarks/games/nano-after-resources.jsonl.gz)
- [ascii-bench.txt](https://superweb.to/benchmarks/games/ascii-bench.txt)
- [perf-report.txt](https://superweb.to/benchmarks/games/perf-report.txt)
- [perf.log](https://superweb.to/benchmarks/games/perf.log)
- [environment.json](https://superweb.to/benchmarks/games/environment.json)
