主要託管 LLM API 的實測 TTFT 與吞吐量基準:深入分析與工程拆解
Real-World TTFT & Throughput Benchmarks across Major LLM Hosted Endpoi: Deep Dive & Engineering Breakdown
NeuBench 速度測試顯示,主要託管 LLM 供應商使用相同模型權重時,生成吞吐量最高可相差 40%。文章拆解 TTFT 與 token 吞吐量受推理架構、硬件互連及 KV cache 處理影響的機制,並提供以 Python 監測串流推理指標的程式碼,以及提高吞吐量與降低 TTFT 之間的工程取捨。
Decoding LLM Inference Economics: Real-World TTFT & Throughput Benchmarks Across Hosted APIs
When architecting production LLM systems, engineers spend weeks fine-tuning prompts, evaluating embedding strategies, and optimizing RAG pipelines. Yet, many treat the hosted inference endpoint as a black-box utility: you send an HTTP POST request, and tokens trickle back.
This abstraction is dangerous.
Recent empirical data from NeuBench speed testing reveals token throughput variances of up to 40% across major hosted LLM providers for the exact same model weights. In distributed systems handling tens of thousands of requests per minute, a 40% swing in generation throughput doesn't just impact latency—it dictates your infrastructure costs, alters user experience, and breaks tight HTTP timeout budgets.
In this deep dive, we will unpack the mechanics of Time to First Token (TTFT) and token throughput, examine the architectural bottlenecks causing these massive variances, and look at how to engineer resilient inference pipelines.
The Core Architecture & Mechanism
To understand why two identical open-weights models hosted by different providers exhibit vastly different performance characteristics, we must inspect the serving architecture.
LLM inference is memory-bandwidth bound, not compute-bound. It operates in two distinct phases:
- Prefill Phase (Compute-Bound): The prompt is processed in parallel. The engine ingests the input tokens, computes the Key-Value (KV) cache for the entire prompt, and generates the very first output token. This dictates your Time to First Token (TTFT). TTFT is heavily influenced by batching efficiency, GPU interconnect (NVLink bandwidth), and sequence length.
- Decoding Phase (Memory-Bandwidth Bound): The model generates tokens autoregressively, one by one. Each step requires reading the entire model weights and the growing KV cache from High Bandwidth Memory (HBM) to the SRAM of the accelerator. This dictates your Token Throughput (Tokens/Second).
[Client Request]
│
▼
[Load Balancer / API Gateway]
│
▼
[Inference Engine (vLLM / TGI / TensorRT-LLM)]
├── Prefill Phase ──> Computes KV Cache ──> [TTFT Metric]
└── Decode Phase ──> Autoregressive Loop ──> [Throughput Metric]
The Culprits Behind Variance
Why do providers differ by 40%? The delta comes down to infrastructure engineering choices:
- Inference Engines: Proprietary runtimes versus optimized open-source engines like vLLM, TensorRT-LLM, or TGI. Features like PagedAttention (mitigating memory fragmentation) and Continuous Batching (dynamic batching at the iteration level) drastically alter throughput.
- Hardware Tiering & Interconnect: A100 vs. H100 clusters, PCIe vs. SXM5 form factors, and cluster networking fabrics (InfiniBand vs. standard Ethernet) directly limit tensor parallelism communication overhead.
- KV Cache Quantization & Offloading: Providers utilizing FP8 or INT8 KV cache compression can pack larger batch sizes into HBM, reducing memory bandwidth pressure and boosting throughput at a negligible accuracy cost.
Python Implementation: Monitoring TTFT and Throughput
To engineer around provider variance, your application layer must actively monitor real-time inference metrics. Below is a production-grade asynchronous Python snippet utilizing httpx to stream completions while capturing exact TTFT and end-to-end token throughput.
import asyncio
import time
import json
import httpx
async def benchmark_llm_stream(api_url: str, api_key: str, model: str, prompt: str):
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"stream": True,
"stream_options": {"include_usage": True}
}
ttft = None
token_count = 0
start_time = time.perf_counter()
first_token_time = None
async with httpx.AsyncClient(timeout=60.0) as client:
async with client.stream("POST", api_url, headers=headers, json=payload) as response:
if response.status_code != 200:
raise Exception(f"API Error: {response.status_code} - {await response.aread()}")
async for line in response.aiter_lines():
if not line.startswith("data: "):
continue
data_str = line[6:].strip()
if data_str == "[DONE]":
break
chunk = json.loads(data_str)
# Check for usage stats in the final chunk (OpenAI standard)
if "usage" in chunk and chunk["usage"]:
token_count = chunk["usage"].get("completion_tokens", token_count)
continue
choices = chunk.get("choices", [])
if choices:
delta = choices[0].get("delta", {})
if "content" in delta and delta["content"]:
if ttft is None:
first_token_time = time.perf_counter()
ttft = first_token_time - start_time
if "usage" not in chunk:
token_count += 1
end_time = time.perf_counter()
total_time = end_time - start_time
generation_time = end_time - first_token_time if first_token_time else total_time
throughput = token_count / generation_time if generation_time > 0 else 0
return {
"ttft_seconds": ttft,
"total_latency_seconds": total_time,
"completion_tokens": token_count,
"throughput_tokens_per_sec": throughput
}
# Example Usage:
# asyncio.run(benchmark_llm_stream("https://api.provider.com/v1/chat/completions", "sk-...", "meta-llama/llama-3-70b-instruct", "Explain quantum computing in 3 paragraphs."))
Key Engineering Trade-offs
When designing high-throughput LLM pipelines, you must navigate several architectural trade-offs:
| Dimension | Option A: Maximize Throughput (High Batch Size) | Option B: Minimize TTFT (Low Batch Size / Dedicated) |
|---|---|---|
| Latency Profile | Higher TTFT due to queuing; consistent inter-token latency. | Lowest possible TTFT; vulnerable to queue spikes. |
| Cost Efficiency | High (Optimal GPU utilization, amortized memory overhead). | Low (Underutilized HBM bandwidth, high cost per token). |
| Memory Pressure | High KV cache fragmentation risk if engine lacks PagedAttention. | Low active memory footprint per instance. |
| Use Case | Batch data processing, offline summarization, RAG ingestion. | Real-time chat interfaces, voice bots, autocomplete. |
Choosing a provider solely based on static benchmark leaderboards ignores these realities. A provider optimized for high-throughput batch inference will fail a synchronous conversational application requiring sub-200ms TTFT.
Production Takeaways & Best Practices
- Implement Multi-Provider Fallbacks & Dynamic Routing: Never rely on a single hosted endpoint. Build a routing layer that tracks live provider health. If Provider A's TTFT degrades past a defined SLA threshold, seamlessly failover to Provider B.
- Optimize Prompt Caching: Modern inference runtimes support prefix caching. Structure your system prompts to keep static instructions at the absolute beginning of the prompt, allowing providers to cache the prefill KV states and slash TTFT by up to 50%.
- Calibrate Client-Side Timeouts: Account for variable prefill times. If your application sends massive context windows (e.g., 32k tokens), your TTFT will naturally spike. Set dynamic timeouts scaled to input token length rather than static HTTP limits.
- Continuously Audit Real-World Speed: Static pricing pages don't reflect live infrastructure congestion.
Continuous Evaluation Note
Because cloud provider performance fluctuates based on regional demand, hardware updates, and software stack changes, engineering teams need empirical, continuous telemetry. You can track live benchmark scores, TTFT percentiles, and token speed across 260+ models on NeuBench. Integrate these empirical benchmarks into your architectural decision matrix to optimize both your production latency SLAs and unit economics.
來源:DEV Community · dev.to