n4nAI

Self-hosted Llama 3.3 70B: throughput at 50 concurrent users

Analysis of Llama 3.3 70B self-hosted concurrency throughput at 50 users: hardware, batching, tradeoffs vs API, and a decisive takeaway for engineers.

n4n Team5 min read1,084 words

Audio narration

Coming soon — every post will get a voice note here.

Llama 3.3 70B self-hosted concurrency throughput at 50 concurrent users is the metric that separates a demo from a production service. If you can’t hold p99 latency under control when five dozen sessions hit the model at once, the rest of your pipeline doesn’t matter. This analysis breaks down what it actually takes to serve that model at that load, where the bottlenecks hide, and when you should just call an API instead.

Thesis: self-hosting is viable, but only with continuous batching and quantization

You can serve Llama 3.3 70B to 50 concurrent users on two A100 80GB cards if you run a modern inference server with tensor parallelism and fp8 weights. Without quantization or a batcher that fuses requests, you will either OOM or watch tail latency climb past ten seconds. The decision is not “self-host vs API” in the abstract; it’s about whether you can absorb operational complexity for data locality and predictable spend. The Llama 3.3 70B self-hosted concurrency throughput at 50 concurrent users depends almost entirely on scheduler quality, not raw FLOPS.

Hardware and quantization baseline

Memory math

Llama 3.3 70B in fp16 needs ~140GB of VRAM just for weights. That excludes KV cache and activations. fp8 cuts that to ~70GB, fitting two A100 80GB cards with headroom for a modest KV cache. int4 (via AWQ or GPTQ) drops to ~35GB, letting a single A100 80GB handle the weights and leaving room for larger contexts.

Activation memory scales with batch size and sequence length. At 50 users with 2K context, expect a few GB of transient buffers. The real constraint is KV cache: each token stored per layer consumes 2 * n_layers * head_dim * n_kv_heads * batch * seq_len bytes. With paged attention, this becomes a tunable budget rather than a static allocation.

# vLLM launch with fp8 (requires compute capability 80+ and recent vLLM)
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3.3-70B-Instruct \
  --tensor-parallel-size 2 \
  --dtype fp8 \
  --max-num-seqs 64 \
  --max-model-len 8192

The --max-num-seqs 64 cap is what lets the scheduler admit 50 concurrent requests without thrashing.

Serving stack

Use vLLM or TGI. Both implement continuous batching and paged attention. Raw Hugging Face transformers with a naive loop will not survive 50 users; it processes one request at a time or wastes memory with static padding. If you insist on custom code, you inherit the scheduler burden that these projects spent years hardening.

What 50 concurrent users really means

Concurrent users are not 50 open WebSocket connections. They are 50 sequences actively decoding tokens in the same scheduler step. A chat app with 50 idle connections and one typing user imposes near-zero load. A benchmark that fires 50 requests simultaneously and waits for all completions imposes maximum load.

Real traffic is bursty. You might average 12 active decodes with occasional spikes to 50. Size for the spike, not the mean. The Llama 3.3 70B self-hosted concurrency throughput at 50 concurrent users should be measured under your actual arrival distribution, not a synchronized barrier.

Concurrency behavior at 50 users

Continuous batching mechanics

A 70B decoder is memory-bandwidth bound during decode. Each generated token reads the full weight set from HBM. With fp8 weights at 70GB and ~3–4 TB/s aggregate bandwidth across two A100s, the raw ceiling is tens of tokens per second per GPU for decode. Continuous batching hides this by overlapping the memory reads for many sequences: while one user’s token is computed, the scheduler fetches weights for the next user’s token.

At 50 concurrent users with short prompts (≤1K tokens) and 2K output caps, the scheduler keeps the batch full. Throughput becomes a function of batch size and context length, not of per-request serialization.

Latency vs throughput tradeoff

Time-to-first-token (TTFT) scales with prompt processing cost. At 50 users, the prefill stage contends for compute. If all 50 arrive simultaneously, TTFT degrades compared to sparse arrival. You mitigate this with --max-num-batched-tokens limits and by shedding load when KV cache exhausts.

# Client load test snippet using OpenAI SDK
import asyncio, openai

client = openai.AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

async def hit():
    resp = await client.chat.completions.create(
        model="meta-llama/Llama-3.3-70B-Instruct",
        messages=[{"role": "user", "content": "Summarize: " + "x"*800}],
        max_tokens=256,
    )
    return resp.choices[0].message.content

async def main():
    tasks = [hit() for _ in range(50)]
    await asyncio.gather(*tasks)

asyncio.run(main())

Run this with uvloop and measure p50/p99 with your own hardware. Don’t trust a number from a blog that isn’t your silicon.

Interpreting results without fabricating numbers

You will see aggregate token throughput rise as concurrency increases until KV cache or bandwidth saturates. Beyond that point, adding users increases latency linearly while throughput flatlines. The inflection is where your 50-user target should sit—with margin.

If you observe TTFT > 2s at p99 with steady 50-user load, your prefill is underprovisioned. Either reduce --max-model-len, shard across more GPUs, or cap concurrent requests at the ingress.

Scaling beyond a single node

When 50 users becomes 200, a single 2-GPU node is insufficient. Options:

  • Pipeline parallelism in vLLM across 4–8 GPUs for one model replica.
  • Multiple replicas behind a stateless router; each handles ~40 concurrent users.
  • Speculative decoding with a small draft model to lift decode throughput, though 70B gains are modest.

Router logic must respect session affinity only if you cache prefixes; otherwise round-robin is fine.

Tradeoffs vs calling an API

Self-hosting gives you deterministic data paths and no per-token markup. You pay for GPUs whether they’re idle or saturated. An API endpoint shifts that to variable cost but introduces network egress, provider rate limits, and opaque scheduling.

If operating GPUs isn’t your team’s core competency, an OpenAI-compatible gateway such as n4n.ai exposes 240+ models with automatic fallback when a provider degrades, and honors client routing directives. That trades fine-grained batch control for zero hardware ops. For a 50-user steady load, the GPU cost at fp8 on reserved instances often undercuts API spend after ~3–4 months, but only if your utilization stays high.

Conversely, if your traffic spikes to 50 users for one hour daily, API cost will beat a dedicated node sitting idle 23 hours. The break-even is utilization, not model size.

Configuration details that matter

  • KV cache precision: Keep it in fp8 or fp16. fp32 KV cache halves your context capacity.
  • Chunked prefill: Enable in vLLM to avoid head-of-line blocking when a 8K prompt lands among 1K ones.
  • Health checks: A /health endpoint that returns {"ready": false} under memory pressure lets your LB shed.
{
  "scheduler": {
    "max_num_seqs": 64,
    "max_num_batched_tokens": 4096,
    "chunked_prefill": true
  },
  "kv_cache": {"dtype": "fp8"}
}

Decisive takeaway

Run Llama 3.3 70B self-hosted on 2× A100 80GB with fp8 and vLLM if you have steady ≥40 concurrent users, can tolerate GPU ops, and need data isolation. Size --max-num-seqs to your real concurrency plus headroom, load test with your own prompts, and cap ingress at the saturation point. If your traffic is spiky or your team ships models not infrastructure, use a gateway and skip the cooling fans. Either way, measure TTFT at p99, not average throughput—concurrency is a latency problem disguised as a bandwidth problem. The Llama 3.3 70B self-hosted concurrency throughput at 50 concurrent users is achievable, but only when the scheduler is treated as a first-class component, not an afterthought.

Tagsllama-3-3self-hosted-llmconcurrencythroughput-benchmark

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All self-hosted vs api performance posts →