Benchmark → Compare

Compare

Print multiple Results side-by-side in a columnar table. The target row shows what each column is measuring so mixed comparisons — RoExpress, named ports, and vanilla remotes together — are easy to read at a glance.

bench:Compare()

bench:Compare(results: { Results }) → ()

Accepts an array of Results returned by any run method. Prints a formatted columnar table to the Studio output window. Each column is one benchmark run identified by its label and target.

local bench     = RoExpress("Benchmark")
local rs        = game:GetService("ReplicatedStorage")
local combatNet = RoExpress("Network", "combat")
local echoFunc  = rs:WaitForChild("EchoFunc")

bench:Compare({
    bench:Run("player/123",                { label = "Main GET"   }),
    bench:RunOnPort(combatNet, "shoot/1", { method = "POST", label = "Port POST"  }),
    bench:RunRemoteFunction(echoFunc,      { label = "Vanilla RF"  }),
})

Comparison table output

── ─────────────────────────────────────────────────────────────────
   metric      Main GET          Port POST         Vanilla RF
   ───────────────────────────────────────────────────────────────
   min         4.21 ms           5.10 ms           8.44 ms
   avg         6.83 ms           7.22 ms           12.31 ms
   median      6.10 ms           6.90 ms           11.80 ms
   p95         12.44 ms          13.01 ms           22.10 ms
   p99         18.07 ms          19.44 ms           28.77 ms
   max         18.71 ms          20.01 ms           30.22 ms
   ───────────────────────────────────────────────────────────────
   errors      0/20              0/20              0/20
   req/s       8.3               7.9               4.1
   target      RoExpress         RoExpress Port    RemoteFunction
── ─────────────────────────────────────────────────────────────────

Interpreting p95 vs p99

p95 (95th percentile) means 95% of your measured requests finished within that time. It captures the "typical worst case" — one request in twenty lands at or above this value. For most gameplay systems, p95 is the right threshold to enforce.

p99 (99th percentile) captures the tail — one request in a hundred lands at or above this value. High p99 with a low avg indicates occasional spikes: JIT cold starts, GC pauses, or Roblox rate-limit jitter. Use p99 to detect these outliers, but don't set SLA targets against it with low iteration counts (you need at least 100 samples for a meaningful p99).

MetricWhat it tells youWhen to use it
avgMean RTTOverall throughput character; hides outliers
medianp50 — half of requests are fasterTypical player experience
p95Typical worst case (1-in-20)Pre-deploy gate, SLA thresholds
p99Tail latency (1-in-100)Spike detection; needs ≥100 iterations
maxSingle worst requestIdentifying timeouts; sensitive to outliers

Using Compare in pre-deploy checks

Run Compare from a LocalScript in Studio Play mode before shipping a build. Keep a baseline result (or hard-coded thresholds) and assert against p95:

local bench   = RoExpress("Benchmark")
local result  = bench:Run("player/123", { iterations = 50 })

bench:Print(result)

if result.p95 > 20 then
    warn("p95 regression detected:", result.p95, "ms (threshold: 20 ms)")
end
Do not run Benchmark in production. Each measured request is a real remote call from a real player connection. Use Benchmark in Studio Play mode or a dedicated test place only.

bench:Destroy()

bench:Destroy() → ()

Drops the internal Network reference. Does not destroy the Network instance — the caller owns it.

See also

← Benchmark  ·  Run | collecting Results to pass to Compare  ·  Network | the instance passed to Benchmark  ·  TokenBucket | rate limits that affect results