# Comparison & Benchmarks

> Sourced voice benchmarks: calls per CPU vs Asterisk, QUIC turn latency, and HTTP/3 throughput. The page includes the cost model, the methodology, and the caveats.

This page collects the numbers that other pages point at. It states the
scenario and the hardware behind each number. It also states which numbers are
measurements and which are models. One rule governs all of it: **measure your
own workload before you quote a figure.** Every number below came off a
specific rig under a specific load. Your numbers will differ. The tooling to
measure your own numbers is linked at the end.

> **NOTE:**
> **Methodology, up front.** Each result names its scenario, its hardware, and a
> status. A **measured** result is a real number off a real rig. Trust it for the
> scenario it describes, not as an SLA. A **prototype** result comes from a
> purpose-built test harness, not the production stack. Treat it as directional
> evidence for a design choice. A **model** is a calculator, not a measurement.
> It has assumed inputs that you must replace with your own. We do not publish a
> headline dollar figure or a peak packet-rate number. The harnesses that would
> produce those numbers still stub parts of the path. When in doubt, run the
> measurement again on your traffic.

## How to read these numbers

| Result | Status | Scenario | Hardware |
| --- | --- | --- | --- |
| Calls per %CPU vs Asterisk | **Measured** | Inbound answer + G.711 playback, media-plane load | GCE `c4-highcpu-8` |
| QUIC vs TCP turn latency | **Prototype** | End-of-speech → agent-speech, WAN 50 ms RTT / ~1% loss | Purpose-built quic-go rig |
| Browser diversion tax | **Prototype** | Loopback capture → encoded frame round trip | Localhost Chrome |
| HTTP/3 stream throughput | **Measured** | 1 connection × 100 concurrent streams | Single box |
| Media fan-out CPU vs a WebRTC SFU | **Measured** | 1 publisher → N subscribers | Localhost **and** GCE `c4-highcpu-8` |
| $/1k-call-minute | **Model** | Cost calculator, assumed media inputs | — |

## Media plane: calls per unit of CPU

The gateway is a modular, shard-per-core engine with an **in-process RTP media
plane**. The same process that terminates SIP also mixes and paces the audio.
There is no per-call transcode hop. On a fully NIC-owned deployment, the media
uses a zero-copy kernel-bypass fast path. As a result, the engine carries far
more concurrent audio per CPU than a channel-driver PBX.

On identical hardware (GCE `c4-highcpu-8`), an inbound-answer + G.711-playback
media-plane test measured:

| Engine | Sustained calls | Box CPU | Calls per %CPU |
| --- | --- | --- | --- |
| FirstByte Voice | **3,500** | **8.38%** | ~418 |
| Asterisk 20.6 (`chan_pjsip`) | 5,000 | 44.75% | ~112 |

That is approximately **3.7× more calls per unit of CPU**. Two caveats keep
this honest:

- **Both systems capped on SIP call *setup*, not media.** FirstByte's
  signalling runs on a single shard (shard-0). That shard is what pinned the
  result at 3,500 in this test. The media plane itself sat near-idle with large
  headroom (media load spread dead-even across all 8 shards, ~300 Mbps NIC
  egress). These are **floor** numbers for the media plane. The ratio is a
  media-per-CPU advantage, not a max-concurrent-calls promise.
- **It is a synthetic G.711-playback scenario.** Your codec mix, recording, AI
  attachment, and trunk topology all move the number.

A migration off Asterisk or FreeSWITCH re-points trunks. It does not import a
dialplan. See [From Asterisk / FreeSWITCH](/modalities/voice/migration/from-asterisk-freeswitch).

## Transport: QUIC turn latency vs TCP

This is the hop where the transport choice shows up as a number. A controlled
**prototype** rig measured the same turn (end-of-user-speech to
start-of-agent-speech) over QUIC and over a TCP+TLS baseline. The rig ran over
a WAN link (**50 ms RTT, ~1% loss**):

| Turn latency (prototype, 50 ms RTT / ~1% loss) | QUIC | TCP + TLS |
| --- | --- | --- |
| p50 | **91.2 ms** | 193.0 ms |
| p99 | **91.9 ms** | 193.8 ms |

QUIC approximately **halves** the turn and keeps the tail flat. The p99 sits
right on the p50 because head-of-line blocking is gone. Under concurrency
(c = 20), the gap widens where it hurts most. Time-to-first-token p50 was
**93.9 ms vs 143.6 ms** (−35%). The p99 was **149.2 ms vs 1,197.9 ms**, an ~8×
better tail. QUIC also gave ~60% more throughput. The signalling-gateway proxy
added ~2 ms flat out to 100 concurrent.

> **WARNING:**
> These are **prototype** numbers from a purpose-built rig, not a measurement of
> the production stack. Treat them as directional evidence for the transport
> choice, not a latency SLA. The productionized transport uses the same `:443`
> QUIC plane. Measure your own turn with
> [Latency Breakdown](/modalities/voice/observability/latency-breakdown) before
> quoting a figure.

### The browser diversion tax

The diversion path reuses the browser's encoder and sends the tapped Opus
frames over QUIC instead of native SRTP/UDP. This path is not free, but it is
close to free. In the localhost diversion **prototype**, the JS-layer round
trip measured **1.20 ms** over QUIC versus **0.84 ms** for raw WebRTC SRTP/UDP.
That is a **+0.36 ms** tax for the tap and the QUIC hop. The tax is noise next
to a single 20 ms Opus frame. In return you get a single `:443` plane, no SFU,
and encoded-frame E2EE readiness. See
[WebRTC diversion](/modalities/voice/transport-web/webrtc-diversion) for how the
tap works.

## Control plane: HTTP/3 stream throughput

The same QUIC transport carries the control plane (call setup, signalling RPC).
The test ran a single connection that multiplexed **100 concurrent streams**.
Targeted work on the stream scheduler took request throughput from
**1.3K → ~47K req/s**, a ~36× improvement. That result is on par with a leading
reference HTTP/3 implementation. Head-of-line-free multiplexing lets one
connection carry many concurrent call-control operations. A TCP path shows a
tail blowup under the same concurrency (see the c = 20 tail above).

## Media fan-out: CPU vs a WebRTC SFU

For one-to-many audio/video fan-out (supervisor monitoring, broadcast), the
engine shares one encoded buffer across subscribers. It does not re-encrypt for
each peer. The test compared the engine against **pion** (an open-source WebRTC
SFU) on a 1-publisher → N-viewer fan-out. The localhost result looked lopsided:

| Viewers | FirstByte CPU | pion CPU | Ratio |
| --- | --- | --- | --- |
| 10 | 4.0% | 8.7% | 2.2× |
| 30 | 8.3% | 17% | 2.0× |
| 60 | 12.3% | 34% | 2.8× |

Per-viewer CPU **decreased** as subscribers grew (0.40% → 0.21%, a sublinear
shared-buffer fan-out). Pion stayed flat at ~0.57% (linear per-peer SRTP).

> **WARNING:**
> **We do not stand behind a "many-times-more-efficient" headline. The honest
> finding is near-parity on real hardware.** The same test ran again on a real
> 2-host GCE pair (`c4-highcpu-8`, isolated system-under-test, real NIC). That
> run collapsed the gap. Every configuration landed at ~**0.2–0.3% CPU per
> viewer**: pion at 0.23–0.255%, single-shard FirstByte at 0.21% (most
> efficient but single-core capped), and an 8-shard cross-shard config at 0.31%
> (a cross-shard tax *above* pion). The localhost 2–3× was an artifact of a
> throttled laptop. **On identical hardware, per-viewer CPU is approximately at
> parity. The cost advantage is the hosting and egress model, not raw CPU.**

## Cost: a model, not a headline number

There is no measured `$/1k-call-minute` figure to publish. The cost harness
that would produce one still **stubs parts of the media-spawn path**. Its
dollar tables are explicitly placeholders. They form a cost *model* that you
parameterize, not a measurement of the running stack.

The model is useful as a *method*. For a given call profile, cost per thousand
call-minutes is a function of three terms:

- **Compute** — CPU-seconds per call-minute, times your instance's $/CPU-hour.
  The media-plane efficiency above is the lever here.
- **Egress** — bytes per call-minute, times your provider's $/GB. Codec choice
  sets this term: G.711 is ~64 kbps per leg, and Opus narrowband is far less.
- **AI runtime** — per-minute ASR/LLM/TTS or speech-to-speech provider spend.
  This term usually dominates, and your vendor's pricing sets it entirely.

The engine's efficiency moves the first two terms. It does not move the third.
On most AI-voice workloads, the runtime term is larger than compute and egress
combined. That is why we lead with "measure your own" rather than a dollar
chart. The model needs per-tenant, per-modality byte and bandwidth accounting
for real inputs. That accounting **is** shipped as a capability. See
[Dashboards](/modalities/voice/observability/dashboards).

> **NOTE:**
> **Your results will vary — a lot.** Codec, recording, region, instance type, AI
> provider, and concurrency each move cost by more than any transport
> optimization. Treat the three terms above as the calculator. Plug in your own
> measured inputs. Do not carry a single headline number between workloads.

## Measure your own

You can reproduce every number here on your traffic. That is the only way to
get a figure that you can quote to your own stakeholders.

  1. **Turn latency**
Log `end-of-user-speech → first-audio-out` per turn and take p50/p95/p99.
    The five hops and how to attribute each are in
    [Latency Breakdown](/modalities/voice/observability/latency-breakdown).
  2. **Media quality under load**
Watch MOS, jitter, and packet loss as concurrency climbs. The media plane
    scores its E-model MOS per call. See
    [MOS, Jitter & Loss](/modalities/voice/observability/mos-jitter-loss).
  3. **Cost inputs**
Read per-tenant, per-modality bytes and bandwidth off the traffic
    dashboard. Combine them with your instance and provider pricing in the
    model above. See [Dashboards](/modalities/voice/observability/dashboards).

## Related

  - **[Latency Breakdown](/modalities/voice/observability/latency-breakdown)** — The five hops of a voice turn and how to measure yours.
  - **[MOS, Jitter & Loss](/modalities/voice/observability/mos-jitter-loss)** — Media-quality metrics scored per call under load.
  - **[From Asterisk / FreeSWITCH](/modalities/voice/migration/from-asterisk-freeswitch)** — The migration behind the calls-per-CPU comparison.
  - **[Architecture](/modalities/voice/concepts/architecture)** — The shard-per-core, in-process media design that these numbers come from.
