Measured, not estimated

StyloBot benchmarks

StyloBot runs up to 57 detectors in a wave-gated pipeline. We measure two layers: nanosecond-scale hot paths with BenchmarkDotNet, and end-to-end throughput on the shipped ARM gateway with k6. Both are on the record below.

These figures describe recorded test runs, not a capacity promise. Hardware, upstream behavior, traffic shape, persistence, and enforcement policy all change the result. Every section says exactly what ran.

Gateway throughput on a Raspberry Pi 5

k6 drove constant-arrival load at 30 and 50 requests/second, then a ramping-arrival plateau through 150 and 200 requests/second, against the shipped NativeAOT gateway on entry-level ARM hardware, not a beefy server.

p95 latency vs offered load
55 60 65 70 62.5ms 30 req/s 63.8ms 50 req/s 65.5ms 150 req/s 65.5ms 200 req/s

p95 end-to-end latency is identical at 65.5ms from 50 through 200 requests/second: no latency knee across the tested range.

Core headroom at 200 req/s
~17%
of 4 cores in use to sustain the top tested load.
Failures at 30 & 50 req/s
0%
0 dropped requests across both 15-minute steady-load runs.
30 req/s, steady load
p50
54.6ms
p90
61.5ms
p95
62.5ms
50 req/s, steady load
p50
58.1ms
p90
62.7ms
p95
63.8ms
Memory under load (RSS)
177 MB
30 req/s
190 MB
50 req/s
221 MB
150 req/s
251 MB
200 req/s

Resident memory grows gradually and stays bounded from 30 to 200 requests/second, with no leak signature across the run.

150 req/s: 2.15% of requests came back non-2xx: the load script's own honeypot and attack-probe paths expecting a block, not gateway errors. The gateway log was clean.

200 req/s: 2.03% non-2xx, same benign probe traffic. p95 is identical to the 50 RPS step, so there's no latency knee through 200 RPS.

Hardware: Raspberry Pi 5, Cortex-A76, 4 cores, arm64. Build: NativeAOT gateway allbot-v8.1.7 (bf15f84e). Config: every detector active, action policy logonly, learning writes disabled. That's a clean-throughput profile, not an enforcement-under-attack test. Driver: k6 from a separate host.

What NativeAOT costs, honestly

StyloBot ships as NativeAOT for startup time, memory footprint, and single-file deployment. That trade isn't free everywhere, so BenchmarkDotNet compares the same hot paths under JIT and NativeAOT and puts the cost on the record instead of asserting it.

Hot path JIT NativeAOT Result
Identity vector match (WeightedCosine, TopK=5)
The actual per-visitor hot path. Vectorized code is AOT-neutral or faster.
193.4ns 122.9ns Faster under AOT
Dashboard country-code accessor (guarded, shipped)
An unguarded compiled-delegate version measured 55.3ns under AOT, 20x slower than JIT, because NativeAOT has no dynamic codegen. Guarding it behind a dynamic-code check and falling back to reflection dropped that to 9.8ns.
2.9ns 9.8ns Fixed regression
Adaptive weighting (50 features)
Dictionary-building and LINQ-heavy paths are the AOT cost center: 2.7x slower, 53% more allocation. Documented as a tuning candidate, not hidden.
7615.7ns 20398.2ns Regresses under AOT
Web Bot Auth signature verify (Ed25519)
Crypto-dominated work costs the same either way. The elliptic-curve math, not managed codegen, sets the floor.
20748.7ns 20165.4ns AOT-neutral

Measured with BenchmarkDotNet 0.15.8 on an Apple M5 (osx-arm64). The JIT-to-AOT delta is the point here, not the absolute nanoseconds: the gateway itself ships linux-arm64 on the Pi hardware above. The full ~80-benchmark set, including SIMD, session-vector, and crypto suites, is in the repository.

Core scaling: SQLite vs Postgres

The next run isolates compute from network entirely: same host, same load, gateway CPU allowance stepped through 1, 2, 4, 8, 16, and 32 cores, against both the SQLite and PostgreSQL persistence backends. That answers the question this page can't yet: where does each backend stop scaling, and how much of a curve does moving to Postgres buy you.

In progress Results replace this panel once the sweep completes. No placeholder numbers.
1 core 2 cores 4 cores 8 cores 16 cores 32 cores req/s vs. CPU allowance, awaiting EXP1

Same-host loopback traffic removes the network as a variable so the curve reflects compute scaling alone. We expect the two backends to diverge, since SQLite's single-writer WAL model should plateau earlier than Postgres's connection pool. If they don't, that's the finding.

Which config fits your traffic

A practical starting point, not a benchmark claim, grounded in what we've measured plus how the persistence layer is built (ISessionStore, IConfigurationSource, Redis-coordinated fleets).

What we measure

  • BenchmarkDotNet: in-process detector and pipeline work, allocations, and regression thresholds, JIT and NativeAOT side by side.
  • k6 load tests: end-to-end gateway throughput, latency, connections, persistence, and enforcement under real request traffic.
  • Unit and integration suites: correctness and behavior across the same FOSS and commercial paths customers run.

Read the full context

The repository includes the benchmark harness, scenarios, raw BenchmarkDotNet artifacts, k6 scripts, command lines, hardware notes, and known limits. Use those details when comparing a run with your own deployment.

Benchmark harness →

Performance without a usage-meter surprise

FOSS is free and easy to run. Commercial licences fund the project and add manageability, reporting, and advanced features. Neither option turns requests, seats, bandwidth, or a DDoS into a StyloBot usage bill.