Case study

GPU Benchmarking for Voice Inference

How long a caller waits before a voice agent starts talking, and what it costs — a full speech-to-text, LLM and text-to-speech pipeline on one self-hosted AMD MI300X, NVIDIA GH200 and NVIDIA H100 PCIe, measured against managed APIs.

Role
Product Architect & Builder
Focus
Inference benchmarking
Disciplines
Latency SLOs · capacity · unit economics
Period
October 2026
Scripts & raw results on GitHub

Headline numbers

Measured, not modeled. APIs run 2026-10-01, MI300X and TTS 2026-10-03, H100 PCIe and GH200 2026-10-04. Replies capped at 150 tokens; SLO is TTFT p95 under 500 ms.

331 ms
Speech in → first audio
GH200 · 463 ms MI300X · 641 ms H100 PCIe
3–5×
Sooner than managed APIs
1.6 s Azure · 2.3 s OpenAI
256
Calls within SLO on one GPU
MI300X · 128 GH200 · 64 H100 PCIe
~$0.08
Per 1M output tokens
GH200 $0.077 · MI300X $0.079 vs $2.08 API
0 / 3
Managed APIs meeting SLO
500 ms p95, even at 1 caller

Why benchmark at all

Turning the modeled hardware story into measured numbers.

The Voice AI case study ended on three claims: token generation is memory-bandwidth bound, GPU choice drives reply speed (modeled at 8.5 s on an L4 versus 0.8 s on an H100), and self-hosting only pays off once call volume is steady. All three were derived from profiling and published pricing. None had been put on real data-center hardware against the APIs a team would actually buy.

This benchmark does that. The question a product owner needs answered is not "how many tokens per second" but "how long does a caller sit in silence, how many callers can one GPU hold before that silence breaks the SLO, and what does each answer cost." So the metric is speech in → first audio out, the SLO is 500 ms at p95, and every platform runs the same 12 realistic phone-call turns with the same scripts.

Platform comparison

Voice turn = speech-to-text + LLM time to first sentence. Sessions within SLO = concurrent callers at TTFT p95 under 500 ms.

PlatformTTFT p50TTFT p95Per-user tok/sPeak tok/s (8 users)ASR p50Voice turn$ / 1M outSessions in SLO
AMD MI300X
Llama-3.1-8B · self-hosted
14 ms15 ms1951,051111 ms242 ms$0.079256+ sessions
NVIDIA GH200
Llama-3.1-8B · self-hosted
17 ms18 ms1751,28971 ms224 ms$0.077128 sessions
NVIDIA H100 PCIe
Llama-3.1-8B · self-hosted
41 ms43 ms96671172 ms~434 ms$0.19864 sessions
Azure AI Foundry
gpt-4.1-mini
784 ms1,018 ms213203638 ms1,517 ms$2.080 sessions
OpenAI
gpt-4.1-mini
454 ms574 ms973791,079 ms1,740 ms$2.080 sessions
OpenAI
gpt-4o-mini · reference
518 ms—952971,766 ms2,495 ms$0.7940 sessions

Self-hosted rows run Llama-3.1-8B with the client on the same machine, so they carry no network time; their cost is taken at the highest load where every level up to it met the SLO (MI300X 256 callers, GH200 128, H100 PCIe 64). gpt-4o-mini is from an earlier OpenAI run, kept for reference. The same Llama model can't be compared as a managed service: Azure AI Foundry retired Meta-Llama-3.1-8B-Instruct on 2026-06-13.

Speech in → first audio out

One caller. Where each pipeline spends the silence.

Speech-to-textLLM to first sentenceText-to-speech first audio
NVIDIA GH200
self-hosted, no network
331 ms
AMD MI300X
self-hosted, no network
463 ms
NVIDIA H100 PCIe
self-hosted, no network
641 ms
Azure
Foundry + AI Speech
1,628 ms
OpenAI
4.1-mini + 4o-mini-tts
2,331 ms

Self-hosted rows are one measured pipeline each (p50, 1 caller); the bar shows stage p50s and the label the measured end-to-end p50. API rows add stages measured separately: ASR + LLM from the October 1 runs, TTS from Azure AI Speech (Ava neural) and gpt-4o-mini-tts on October 3.

What changed my mind: in the original voice AI work the voice layer was the expensive part. Here the managed voice layer is actually fast — Azure's TTS is one of the quickest stages on the chart. The managed penalty comes from speech-to-text and the LLM round-trip, which is exactly the part a co-located GPU removes.

The hardware compared

Published vendor specs; rental prices as seen in October 2026.

SpecNVIDIA H100 PCIe
tested
NVIDIA H100 SXM
not yet tested
NVIDIA GH200
tested
AMD MI300X
tested
Memory size80 GB80 GB96 GB (+480 GB CPU memory)192 GB
Memory type and bandwidthHBM2e, ~2.0 TB/sHBM3, ~3.35 TB/sHBM3, ~4 TB/sHBM3, ~5.3 TB/s
Power limit350 W700 Wup to ~1,000 W (GPU + CPU module)750 W
Rental price used~$2.49/hr (Lambda)~$2.99–3.29/hr$1.99/hr (Lambda), ARM CPU$1.99/hr (AMD Developer Cloud)

Token generation for an 8B model is limited mostly by memory bandwidth, so the PCIe H100 is the mid-tier card and the SXM H100 is the like-for-like rival to the MI300X. The GH200 pairs an H100-class GPU (with faster HBM3) with a 72-core ARM Grace CPU on one module. Cost per token is normalised by each GPU's own hourly price, so it is a fair comparison either way.

Three GPUs, one model

LLM only, whole GPU: Llama-3.1-8B-Instruct swept from 1 to 256 concurrent calls.

MetricAMD MI300XNVIDIA GH200NVIDIA H100 PCIe
Rental$1.99/hr$1.99/hr$2.49/hr
TTFT p50 / ITL p50, 1 caller14 ms / 4.9 ms17 ms / 5.4 ms41 ms / 9.4 ms
Highest consistent load within 500 ms p95256 callers128 callers (256 failed 3 of 3)64 callers
Output tok/s at that load7,0267,1403,487
$ per 1M output tokens$0.079$0.077$0.198
Full voice loop, first audio, 1 caller463 ms331 ms641 ms
Burst, 512 in / 128 out, total tok/s18,07130,03318,591
vLLM0.27.1 (ROCm 7.2)0.31.0 (CUDA, ARM)0.30.0 (CUDA)

Against the H100 PCIe the MI300X wins clearly: about 2× the throughput inside the SLO at about 40% of the cost per token. Against the price-matched GH200 it is close: a tie on cost per token, behind on end-to-end latency and on prompt-heavy bursts, and ahead on how many calls one GPU holds inside the SLO (256 vs 128), thanks to 192 GB of memory. GH200 high-load levels ran three times; MI300X and H100 PCIe once. Newer vLLM on the NVIDIA runs may account for part of their lead.

MI300X p95GH200 p95H100 PCIe p95500 ms SLO
1
4
8
16
32
64
128
256

concurrent callers

Red bars miss the SLO. The MI300X stays under 500 ms at 256 calls (418 ms); the GH200 holds to 128 and fails at 256 (748–809 ms); the H100 PCIe crosses it at 128 (548 ms).

CallsTTFT p95Output tok/s
MI300XGH200H100MI300XGH200H100
115 ms18 ms43 ms19517596
422 ms23 ms62 ms561674355
825 ms26 ms72 ms1,0511,289671
1632 ms39 ms122 ms1,9792,3881,256
3254 ms73 ms200 ms2,6643,9322,181
6492 ms95 ms266 ms3,6216,1783,487
128222 ms241 ms548 ms6,5187,1404,201
256418 ms748–809 ms438 ms7,026~7,4504,449

GH200: 64 and 128 callers are the median of 3 runs (pass 3/3); 256 failed all 3 runs (p95 748–809 ms). H100 PCIe: missed the SLO at 128 (548 ms) and passed at 256 (438 ms) on a single run each, so its consistent limit is 64.

The bandwidth thesis, measured three times

An 8B model in 16-bit is about 16 GB of weights, and every token reads all of it. The bandwidth floor is roughly 3 ms per token on the MI300X (~5.3 TB/s), 4 ms on the GH200 (~4 TB/s) and 8 ms on the H100 PCIe (~2.0 TB/s). Measured single-caller decode: 4.9, 5.4 and 9.4 ms. Same order, each close to its floor — the memory-bound behaviour the earlier profiling found at 15.5 ms per token on a much smaller GPU.

Batching is where the money is

On the MI300X, going from 1 to 256 callers raises per-token latency 3.3× (4.9 → 16.2 ms) but output throughput 36× (195 → 7,026 tok/s). Each extra caller rides on weights the GPU was already reading. The prompt-heavy burst test is compute-bound instead, and there the GH200 leads clearly (30,033 tok/s vs ~18,000 for the other two).

The full voice loop under load

Whisper → Llama → Kokoro on one GPU, same models, memory split and single TTS worker on all three. The LLM was never the problem.

NVIDIA GH200

CallsSTTLLMTTSAudiop95
17114107331412
414924209589777
8160247261,1581,345
16187261,8142,2072,494
32365303,3653,8474,829

All times in ms, p50 except the last column.

Fastest stack measured: speech-to-text (71 ms) and text-to-speech (107 ms) are both quicker than on the MI300X (111 / 221 ms); the Grace CPU beside the GPU likely helps TTS text pre-processing.

AMD MI300X

CallsSTTLLMTTSAudiop95
111112221463598
4123146269231,008
8118131,5881,8842,160
16124143,4143,7273,958
32401196,6537,0667,670

All times in ms, p50 except the last column.

STT and the LLM barely move with load; the single TTS worker is the queue.

NVIDIA H100 PCIe

CallsSTTLLMTTSAudiop95
117243220641844
4160504219951,288
8160511,4081,9652,122
16178513,3133,9014,218
32362597,0037,6258,625

All times in ms, p50 except the last column.

TTS matches the MI300X (~220 ms); the gap is speech-to-text (172 vs 111 ms) and the LLM (43 vs 12 ms first token).

Whisper-large-v3-turbo (20% of GPU memory) → Llama-3.1-8B (60%) → Kokoro-82M, one TTS worker. On every GPU, STT and the LLM barely move with load while TTS time climbs with each caller: the single TTS worker is the queue.

Trade-off and lesson: a GPU benchmark that only measures the LLM would have said "128–256 callers per GPU." The full loop says first audio passes 500 ms by 4 callers with one TTS worker — on every GPU tested. Capacity planning has to follow the slowest stage, and here that is an 82M-parameter voice model, not the 8B LLM.

Shared GPU: voice next to a batch job

8 voice callers alongside 32 long batch jobs (~1,500 words in, 512 out).

PhaseVoice TTFT p50Voice TTFT p95vs aloneBatch out tok/sCost split voice / batch
H100 PCIe
Voice alone53 ms89 ms1.0×——
With batch neighbour91 ms131 ms1.5×2,824$0.00019 / $0.00441
Neighbour, voice at higher priority90 ms132 ms1.5×2,826$0.00018 / $0.00430
GH200
Voice alone18 ms26 ms1.0×——
With batch neighbour24 ms40 ms1.6×5,034$0.00005 / $0.00178
Neighbour, voice at higher priority26 ms33 ms1.3×5,057$0.00005 / $0.00171

A batch neighbour made voice 1.5–1.6× slower but still inside the SLO. On the H100 PCIe, vLLM request priority changed nothing: it only reorders a queue, and with free KV-cache memory nothing queued. On the GH200 it trimmed voice p95 from 40 to 33 ms. Cost is split by processed tokens; the batch tenant used ~96% of them.

Trade-off and lesson: a priority flag is not isolation. If voice has to share a GPU with batch work, the guarantee has to come from separate capacity — MIG partitions, a separate replica, or a cap on the batch tenant.

Cost per 1M output tokens

APIs at list price. GPUs at $/hr ÷ throughput at their consistent SLO load — a floor, since real utilisation is lower.

AMD MI300X
Llama-3.1-8B
$0.079
NVIDIA GH200
Llama-3.1-8B
$0.077
NVIDIA H100 PCIe
Llama-3.1-8B
$0.198
Azure AI Foundry
gpt-4.1-mini
$2.08
OpenAI
gpt-4.1-mini
$2.08
OpenAI
gpt-4o-mini
$0.794

Managed text-to-speech, first audio

ProviderCallersp50p95SentenceErrors
Azure AI Speech1111 ms296 ms189 ms0/32
Azure AI Speech476 ms232 ms159 ms10/32 · 429
Azure AI Speech851 ms243 ms129 ms16/32 · 429
OpenAI gpt-4o-mini-tts1591 ms1,508 ms1,387 ms0/32
OpenAI gpt-4o-mini-tts4478 ms818 ms1,173 ms0/32
OpenAI gpt-4o-mini-tts8408 ms600 ms1,046 ms0/32

From Azure Cloud Shell, 32 requests per level. Azure ran on the free F0 tier, which rate-limits concurrent requests (HTTP 429), so only its 1-caller row is clean.

Where the MI300X wins, where it doesn't, and what it costs you

More calls per GPU on memory-bound work, a tie on cost with the GH200, paid for in engineering time.

Bandwidth sets token speed

Generating each token reads the whole model from memory, so decode speed tracks memory bandwidth: ~5.3 TB/s on the MI300X, ~4 TB/s on the GH200, ~3.35 TB/s on the H100 SXM and ~2.0 TB/s on the H100 PCIe. That shows up as lower TTFT and ITL.

192 GB holds more calls

Memory left after the weights holds each live conversation's KV cache. More room means more callers batched at once before anyone queues: 256 callers inside the SLO on the MI300X, against 128 on the 96 GB GH200.

The price is partly strategy

AMD Developer Cloud ($1.99/hr) is AMD's own developer programme with DigitalOcean, likely priced to win developers. Lambda prices the GH200 at the same $1.99/hr, which is why the two tie on cost per token.

Trade-offAMD MI300X (ROCm)NVIDIA (CUDA)
Software maturityMainstream stacks (vLLM, PyTorch) work, with rough edgesDefault target for nearly all AI software
Issues hit in this benchmarkMIOpen re-tuning (5.5 s per sentence until disabled), missing torchcodec, 2 of 8 TTS processes silently on CPUOne missing Python package (pyarrow)
Optimised toolingFewer tuned kernels; no TensorRT-LLMTensorRT-LLM, mature FP8, tuned attention kernels
AvailabilitySmaller cloud footprint; account GPU limitsOffered by almost every cloud
Sharing one GPUPartition modes exist, less usedMIG mature, well supported in Kubernetes
Scaling across GPUsInfinity FabricNVLink / NVSwitch, more proven at large scale
Engineering timeMore time debugging the stackMostly works out of the box

Limits of this result: the H100 tested was the bandwidth-limited PCIe card; an 8B model is the MI300X's easiest case, and its 192 GB matters most for 70B+ models that need two H100s; prompt-heavy (prefill) work is compute-bound and favoured the GH200; vLLM versions differed (0.27.1 vs 0.30.0 and 0.31.0). In one line: the MI300X wins on memory, so it holds the most concurrent calls per GPU and beats the H100 PCIe on tokens per dollar; NVIDIA's price-matched GH200 ties it on cost and is faster end to end; and the MI300X's lower price comes with a less mature stack, paid for in engineering time.

What the numbers say

Self-hosted answers 3–5× sooner

One GH200 gets a caller to first audio in 331 ms, an MI300X in 463 ms, an H100 PCIe in 641 ms. Azure AI Foundry + Azure AI Speech take about 1.6 s and OpenAI about 2.3 s — before the self-hosted network hop is even added.

No managed API meets the 500 ms p95 SLO

Every managed run misses it at p95, even with a single caller. With the LLM alone, the MI300X holds it up to 256 concurrent calls, the GH200 up to 128 and the H100 PCIe up to 64.

MI300X vs NVIDIA

Clearly ahead of the H100 PCIe (2× throughput, 40% of the cost per token). Even with the price-matched GH200 on cost, behind it on latency and prompt-heavy bursts, ahead on calls per GPU.

Azure's TTS is the fastest managed stage

Azure AI Speech starts audio in 111 ms — faster than the single Kokoro worker on the MI300X or H100 (~220 ms), and level with the GH200 (107 ms). The self-hosted lead comes from speech-to-text and the LLM.

Cost floor: ~$0.08 vs ~$2.08

Per 1M output tokens at full load, on either the MI300X or the GH200. Idle GPU time raises the self-hosted figure, so the break-even depends on steady call volume — the same utilisation question the original voice AI economics turned on.

Priority scheduling isn't isolation

A batch neighbour slowed voice 1.5–1.6× on both NVIDIA GPUs, still inside the SLO. vLLM request priority did nothing on the H100 PCIe and only trimmed GH200 p95 from 40 to 33 ms. Real isolation needs separate capacity.

Method

Same workload everywhere, failures counted, limits stated up front.

Workload

12 realistic phone-call turns (booking, billing, sales objections), replies capped at 150 tokens (~20 s of speech), 3 warm-up requests, failures counted. Audio from LibriSpeech clips.

Managed stack

Azure AI Foundry gpt-4.1-mini and OpenAI gpt-4.1-mini (Oct 1); Azure AI Speech (Ava neural, F0 tier) and OpenAI gpt-4o-mini-tts (Oct 3). All called from Azure Cloud Shell, so network time is included.

Self-hosted stack

AMD Developer Cloud (MI300X) and Lambda (H100 PCIe, GH200), one GPU each, with all three models sharing that single GPU and the same scripts on every machine.

1× AMD MI300X 192 GB · vLLM 0.27.1, ROCm 7.2shipped1× NVIDIA GH200 96 GB · vLLM 0.31.0, ARMshipped1× NVIDIA H100 PCIe 80 GB · vLLM 0.30.0, CUDAshippedWhisper-large-v3-turbo · 20% GPU memoryshippedLlama-3.1-8B-Instruct · 60% GPU memoryshippedKokoro-82M · one TTS workershippedNVIDIA H100 SXM · flagship-to-flagshipdesignedMultiple TTS replicas per GPUdesigned

Limits of this comparison: different LLMs (8B open model vs. gpt-4.1-mini); self-hosted figures exclude network time; API voice turns sum separately measured stages; Azure TTS above 1 caller was rate-limited; repeats only on the GH200's high-load levels. Scripts and raw results are public at globaledgepathway/voice-inference-benchmark.

Remaining runs

Added here as they complete.

  • NVIDIA H100 SXM: the like-for-like flagship comparisonrun_h100.sh, ~1 hr
  • MI300X re-run on vLLM 0.31 with repeats at high loadsame software as the GH200
  • Network-inclusive self-hosted run from Azure Cloud Shellsame footing as the API rows
  • Several TTS replicas on one GPU, started one at a timereal calls-per-GPU figure

Results

331 ms
Fastest first audio
GH200, 1 caller
256
Most calls per GPU
MI300X, LLM within SLO
~$0.08
Per 1M tokens
GH200 and MI300X tie
26×
Cheaper than gpt-4.1-mini
per 1M tokens, before idle time
1 worker
The real bottleneck
TTS queue on all three GPUs

Key learning: for voice, the benchmark that matters is time to first audio at p95 under concurrent load, measured through every stage. There is no single winning GPU: memory bandwidth and capacity decide how many callers one GPU holds, the rest of the stack decides how fast each one hears a reply, and whichever stage queues first sets the real limit.