Skip to content

Finding Your Server's Optimal Concurrency Limit

Determine the maximum number of concurrent users your LLM server can handle before latency degrades, and identify the optimal operating point for your deployment.

Prerequisites

Step 1: Run a Sweep

The sweep profile automatically tests increasing load levels; starting from a single sequential request, ramping up to maximum throughput, and interpolating several rates in between.

guidellm run \
  --backend kind=openai_http,target=http://localhost:8000 \
  --data kind=synthetic_text,prompt_tokens=512,output_tokens=256 \
  --profile kind=sweep,sweep_size=10 \
  --constraint kind=max_duration,seconds=120 \
  --seed kind=static,value=42 \
  --output kind=json,path=sweep_results.json

This runs up to 10 strategies (synchronous baseline, throughput ceiling, and 8 interpolated rates) for 120 seconds each.

Adjust prompt_tokens and output_tokens to match your actual workload. A chat application might use prompt_tokens=256,output_tokens=512, while a summarization pipeline might use prompt_tokens=2048,output_tokens=128.

Step 2: Read the Results

Open the console output or JSON report. For each strategy, look at these metrics:

Metric What it tells you
output_tokens_per_second (mean) Server throughput — how much work is getting done
time_to_first_token_ms (p50) Median user-perceived wait before they receive a response
time_to_first_token_ms (p99) Worst-case wait — important for tail latency SLOs
inter_token_latency_ms (p50) How smooth the streaming experience feels
request_latency (p50) Total end-to-end time per request

Step 3: Identify the Three Zones

As load increases across the sweep strategies, your server will pass through three zones:

Underutilized: Throughput scales linearly with load. Latency stays flat. Adding more concurrent users makes the server do proportionally more work with no penalty. You are leaving capacity on the table.

Sweet spot: Throughput is still climbing but gains are shrinking. TTFT p50 is creeping up but still acceptable. The server is efficiently batching requests. This is where you want to operate.

Over-saturated: Throughput plateaus or drops. TTFT p99 spikes sharply. The request queue is growing faster than the server can handle. GuideLLM's over-saturation detector will stop the sweep here.

Look for the transition between the first and second zone — the point where TTFT p99 starts rising noticeably while throughput is still increasing. The strategy just before that transition is your optimal operating point.

Step 4: Validate with a Focused Run

Once you have identified a target concurrency (for example, 32 streams from the sweep results), run a longer benchmark at that specific level to confirm the metrics are stable:

guidellm run \
  --backend kind=openai_http,target=http://localhost:8000 \
  --data kind=synthetic_text,prompt_tokens=512,output_tokens=256 \
  --profile kind=concurrent,streams=32 \
  --constraint kind=max_duration,seconds=300 \
  --seed kind=static,value=42 \
  --output kind=json,path=validation_run.json

A 5-minute run at fixed concurrency gives more reliable metrics than the shorter sweep strategies. Compare the validation results to what the sweep predicted — they should be consistent. If TTFT p99 is higher than expected, drop down one concurrency level and re-validate.

Step 5: Test Around the Boundary

To get a precise answer, run a few concurrency levels around your candidate:

guidellm run \
  --backend kind=openai_http,target=http://localhost:8000 \
  --data kind=synthetic_text,prompt_tokens=512,output_tokens=256 \
  --profile kind=concurrent \
  --override profile.streams 24,28,32,36,40 \
  --constraint kind=max_duration,seconds=180 \
  --seed kind=static,value=42 \
  --output kind=json,path=boundary_test.json

This runs 5 sub-benchmarks at tightly spaced concurrency levels. Compare throughput and TTFT p99 across them to pinpoint exactly where the performance degrades.

Making the Decision

How you pick the final number depends on your deployment goal:

Latency-sensitive (chat, real-time): Choose the highest concurrency where TTFT p99 stays under your SLO. If your SLO is 500ms TTFT, and TTFT p99 at 32 streams is 480ms but at 36 streams is 720ms, run at 32.

Throughput-optimized (batch, offline): Choose the concurrency where output_tokens_per_second plateaus. Latency matters less here, so you can push further into the sweet spot.

Production with headroom: Take your chosen concurrency and multiply by 0.8. If your sweet spot is 40 streams, operate at 32. This gives room for traffic spikes without hitting over-saturation.

Example: Interpreting a Sweep

Here is what a real sweep looks like.

Results collected using meta-llama/Llama-3.1-8B-Instruct on a single NVIDIA A100 80GB GPU, served by vLLM with chunked prefill enabled. Workload: 1000 input tokens, 1000 output tokens.

Strategy Concurrency (mean) Req/s (mean) Output Tokens/s TTFT p50 (ms) TTFT p95 (ms) ITL p50 (ms) ITL p95 (ms) Zone
synchronous 1.0 0.1 90.2 79.5 115.2 11.0 11.1 Under-utilized
constant 3.8 0.3 327.7 95.0 100.1 11.5 11.6 Under-utilized
constant 7.1 0.5 569.7 97.7 102.8 12.3 12.4 Under-utilized
constant 10.8 0.8 809.4 100.4 105.8 13.3 13.4 Under-utilized
constant 14.7 1.0 1047.9 100.4 108.4 14.0 14.1 Sweet spot
constant 21.0 1.2 1274.9 107.8 115.9 16.6 16.7 Sweet spot
constant 27.8 1.4 1496.5 117.7 126.6 19.2 19.3 Sweet spot
constant 33.7 1.6 1728.6 123.5 133.9 19.8 19.9 Sweet spot
constant 46.0 1.7 1911.7 132.6 146.6 25.0 25.5 Over-saturated
throughput 509.8 2.1 3217.6 9947.7 21961.3 84.1 106.8 Over-saturated

In this example, throughput scales linearly from concurrency 1 through ~11 (under-utilized). Between concurrency 15 and 34, throughput is still climbing but TTFT and ITL are creeping up (sweet spot). At concurrency 46, throughput gains flatten while latency continues rising — and the throughput strategy shows TTFT exploding to ~10 seconds, confirming over-saturation.

The optimal operating point here is around concurrency 28-34 — throughput is near 1500-1700 tok/s with TTFT p50 still under 135ms and ITL under 20ms. For production with headroom, target concurrency 24-28.