Below we explain where llmbridge sits, how we measure stamps, and provide three benchmarks: one using an external harness, and two of our own.
llmbridge: the open-source gateway in your request path.
Kottos AI: it records the request executions and evaluates the best venues.
The venues: for a single model there are already several venues that offer the inference.
If processing a request is slow, we need to understand what the bottleneck is. We stamp each request at different points so we can find out what needs improvement. We provide a diagram of some timestamps we care about,
x-llmbridge-gateway-us
connection setup: TCP and TLS to the provider, paid on new connections, 0 when pooled. x-llmbridge-connect-us
before t0: the client’s own handshakes and upload. Not ours, not counted.
provider and network: usually most of the request. x-llmbridge-upstream-us
TLS: cipher on send, decipher on receive, counted inside our spans.
See LATENCY.md for more information.
The first is an external harness that runs nine gateways through one methodology we did not write. The other two are ours: llmbridge and LiteLLM each do the full OpenAI↔Anthropic translation against the same backend. LiteLLM is a full router/SDK and does far more than translate; we compare only the gateway hop both perform, never the model's own latency.
Figures on this page were measured over plain HTTP to a local mock backend. TLS and WAN latency are excluded on purpose so the number isolates gateway overhead. The LiteLLM head-to-heads pin LiteLLM 1.95.0, the release they were measured against; the throughput ceiling is the median of 3 cold-boot runs (87k best), the streaming figures the median of 3 as well. A current LiteLLM (1.99) cuts its own added p99 at 100 RPS from 87 ms to 24–30 ms, so read the ratio as roughly 300× against today’s release and 1,000× against the pinned one. Every release is regression-checked against these paths. Re-run on every tagged release, see BENCHMARKS.md to reproduce.
Non-streaming and streaming below measure different units of work and their numbers are not interchangeable. A streaming request holds one connection for an entire generation, so "requests per second" is not its axis. A non-streaming request delivers one payload, so "tokens per second" says nothing about it. We report them separately for that reason.
The harness is public and MIT-licensed, written by Jakub A. Wąsek of ENTERPILOT, who publishes his own runs. Every gateway runs from its public Docker image, one at a time, against the same in-memory Go mock, with a no-gateway baseline: 20,000 requests per variant at concurrency 10, five trials in randomised order, six variants (chat, responses and messages, streamed and not), and a separate throughput sweep from 1 to 256 concurrent. Its unit of work is one request, so it sits beside A and never beside B.
llmbridge sits on the floor on a single core, while the byte pipe itself takes 2.17. This is not our harness: the AI gateway reproducible benchmark by Jakub A. Wąsek of ENTERPILOT. We ran every gateway from its public Docker image against the same in-memory mock, on a 12-CPU laptop with llmbridge added, 2026-09-04. Writing the record is a separate cost and is not in these numbers. The full table with memory, throughput and the two gateways that did not complete, plus setup, exclusions and raw data: the benchmarks page.
| gateway | runtime | non-streaming | streaming | peak req/s | cores used | memory | req/s per 1% CPU |
|---|---|---|---|---|---|---|---|
| llmbridge v0.53.0 ours, Kottos AI | C++ | 0.07 ms | 0.18 ms | 40,677 | 0.98 | 32 MB | 354 |
| llmbridge, translating to Anthropic v0.53.0 | C++ | 0.10 ms | 0.56 ms | 35,298 | 1.02 | 30 MB | 300 |
| GoModel 0.1.86 | Go | 0.47 ms | 0.75 ms | 16,468 | 5.96 | 77 MB | 23 |
| Bifrost 2.0.0 | Go | 0.80 ms | 2.77 ms | 10,043 | 7.90 | 493 MB | 11 |
| Portkey 1.15.2 | Node | 7.56 ms | 27.60 ms | 1,232 | 1.20 | 198 MB | 10 |
| LiteLLM 1.99.1 | Python | 10.77 ms | 46.06 ms | 880 | 7.05 | 9,528 MB | 1 |
| TensorZero 2026.6.0 | Rust | 41.74 ms | 41.03 ms | 6,834 | 0.17 | 132 MB | 16 |
| OmniRoute 3.8.50 | Node | 172.01 ms | 207.37 ms | 58 | 1.08 | 929 MB | 0.4 |
| socat byte pipe the floor | C | 0.06 ms | 0.27 ms | 43,973 | 2.17 | 5 MB | 157 |
How to read it. Added p50 is over the harness’s own no-gateway baseline. That baseline is one bridge traversal and every gateway is two, so each figure carries one hop that belongs to the topology. The socat byte pipe parses nothing and measures exactly that hop, so read every other row against it. llmbridge’s streaming row measured under the pipe by 0.09 ms, which is noise around zero and reads as at the floor, never as faster than copying bytes. The cores column is not optional: GoModel and Bifrost take every core through the Go runtime and LiteLLM runs one worker per core, so throughput quoted without cores flatters them and understates a single-threaded gateway by about an order of magnitude. The last column is throughput per percent of CPU. Coverage: llmbridge serves four of the six variants, refusing the Anthropic messages pair because that translation is not built, and the translating arm refuses responses. A refusal is not a failure. LiteLLM completed 57,690 of 100,000 streamed chat requests inside the window, and Bifrost’s messages/stream failed outright. TensorZero and OmniRoute are configuration problems, not measurements of the software, and belong in no headline. Two divergences from the published harness, stated because they matter: Docker’s default seccomp filter was off for every gateway, since it blocks io_uring and would have put llmbridge on epoll, and the host is a 12-CPU laptop against the 2-vCPU c7i.large behind ENTERPILOT’s tables, so these rows compare within this table and never with theirs. One llmbridge worker throughout. bench/run_enterpilot.sh in the repository reproduces it, and every variant with the resolved image digests is committed as data next to it.
Open-loop, coordinated-omission-corrected load generator against a 200 ms mock backend. One worker each.
llmbridge's ceiling here (~84k RPS) is set by the worker's own CPU on its single thread. llmbridge is single-threaded, so one core is its hard ceiling, and at saturation that thread is 87–92% busy, i.e. out of headroom. (The machine reports ~95% idle only because one busy core is ~8% of 12 logical CPUs; reaching the rest needs --workers N.) Splitting that thread's own CPU time: ~89% kernel, ~7% llmbridge, the biggest kernel slice being the TCP stack at 32.7%, the price of being a TCP proxy. Thermally dependent: 87k cold, 82k at 85 °C. Everything runs over 127.0.0.1. Scaling past this ceiling is --workers N (shared-nothing, SO_REUSEPORT), which nearly doubles it per worker; a separate-host run is on the roadmap.
| RPS | llmbridge added p99 | LiteLLM added p99 | LiteLLM throughput |
|---|---|---|---|
| 100 | 0.080 ms | 87 ms | 100 / 100 ✓ |
| 1000 | 0.047 ms | 14,213 ms | 244 / 1000 (sat) |
| 5000 | 0.041 ms | 18,695 ms | 236 / 5000 (sat) |
Honest methodology: single Linux host (i7-9750H), mock + proxy + load generator co-located, single worker/thread each, plain HTTP, mock backend (the model's latency is excluded on purpose, to isolate gateway overhead). llmbridge is proxy-self-measured, and LiteLLM is client-measured (e2e − backend). The throughput ceiling on this box is the worker's own CPU, at saturation it burns 87–92% of one core, and perf puts ~89% of that in the kernel (32.7% in the TCP stack) against 6.7% in llmbridge's own code. So the ceiling is a single-thread limit, and the headroom is in kernel work per request, not in the translator. The figure is also thermally dependent (87k cold, 82k at 85 °C on this laptop). Fully reproducible. ./bench/run_headtohead.sh and ./bench/saturate.sh. A clean separate-host run is on the roadmap.
“Concurrent streams” means responses in flight at once through one gateway process. 512 streams is 512 simultaneous voice agents or chat replies, each receiving a token every 20 ms.
Both gateways translate the same Anthropic event stream into OpenAI chunks, at 50 tok/s per stream, swept over concurrency. Every number is taken by the same client-side instrument (neither gateway self-reports) and "added" latency is measured against a no-gateway control run at the same concurrency. Single run per concurrency level.
Every column here is what the gateway adds, not what the request took. The no-gateway control's time to first token is 30.7–30.8 ms at every concurrency (that floor is the mock provider's own think time); llmbridge measures 30.6–30.9 ms, i.e. the same number within ±0.2 ms of run-to-run noise, so it adds nothing measurable and absolute TTFT through it stays ~31 ms. LiteLLM holds at 16 streams, then queues. Delivery: llmbridge 99.93–100% of the achievable token stream, LiteLLM 4% at 512 streams. Its ceiling is roughly 1,100 tokens/s per worker regardless of offered load.
TTFT here is time to first token, not first byte: the harness stamps the arrival of the first SSE chunk that actually carries a token, skipping the response headers, so a provider (or gateway) cannot look fast by sending headers early.
| Concurrent streams | llmbridge added / token | LiteLLM added / token | TTFT added vs no gateway llmbridge / LiteLLM | Tokens delivered |
|---|---|---|---|---|
| 16 | 57 µs | 1.4 ms | +0.0 ms / +50 ms | 100.00% / 94% |
| 64 | 55 µs | 191 ms | +0.0 ms / +2.0 s | 99.98% / 40% |
| 256 | 86 µs | 602 ms | +0.0 ms / +10.2 s | 99.94% / 10% |
| 512 | 131 µs | > 2 s | +0.0 ms / +12.6 s | 99.93% / 4% |
512 is where the LiteLLM comparison stops, not where llmbridge does. Held at 16,384 simultaneous streams on one worker. 16,384 voice agents or chat replies in flight at once, 32,774 sockets open, and it delivers 99.96% of offered tokens with zero client-side failures, p50 36–48 µs and p99 under 0.6 ms, while consuming ~31% of a single CPU core and 189 MB of RAM. Eight independent load generators agree within 5%.
Pushed to 24,576 streams it still delivers
99.93% with zero failures, at 42–47% of one core and 272 MB
(p50 50–58 µs, p99 ~1.2 ms). That is not llmbridge's ceiling;
it is the test machine's: 49,152 of the host's 55,536 ephemeral ports are in use, so
measuring higher needs the load generator spread across multiple loopback addresses. We
claim no number above 24,576. Scale beyond one worker with --workers N.
Honest methodology: the provider stamps its own monotonic emission time inside every token, so added latency is arrival − emission, no clock sync, and no assumption about when a token should have arrived, which is what makes coordinated omission impossible here. LiteLLM gets a full unmeasured warm-up round (its cold start is severe), one worker each, order alternated per level. LiteLLM's per-token latency above 2 s exceeds our histogram's range, so we print "> 2 s" instead of a number that would look like a percentile but is really just the maximum. llmbridge never overflowed it. Single co-located host, plain HTTP; absolute tails are a dev-box upper bound. Reproduce: ./bench/run_stream_headtohead.sh, full method, harness and host configuration in BENCHMARKS.md.