Skip to navigation

Effective vs Active Metrics in AIPerf

View as Markdown

A short technical brief on time-weighted throughput, concurrency, and coordinated-omission-aware latency.

TL;DR

AIPerf reports two complementary families of time-weighted metrics:

  • Effective metrics are time-weighted averages of a step function over the full benchmark window. An “average concurrency of 14.6” means that, integrating across every nanosecond from the first credit to the last response, the in-flight request count averaged 14.6.
  • Active metrics are the same time-weighted averages restricted to segments where the relevant phase has at least one request in flight. An “active prefill throughput of 28k tok/s” means: while any request was actually in prefill, tokens were being produced at that rate on average.
  • effective_latency is a separate per-record metric grouped under EFFECTIVE. It is end_ns - credit_issued_ns — the latency a saturating user actually perceives, including time waiting in the credit queue. It is the only AIPerf latency metric that is coordinated-omission-aware.

Rule of thumb: cite Effective when capacity-planning at the workload mix you measured; cite Active when characterizing peak phase intensity; cite effective_latency when reporting user-perceived latency under load that could be saturating.

Why classical record-averages mislead

LLM inference has three measurement traps that simple arithmetic means hide:

  1. Equal-weight averaging over records is biased toward fast requests. A run with one 10-second request and ninety-nine 1-second requests has the same record-arithmetic-mean as a run with one 100-second request and ninety-nine 1-second requests, even though the first run is far healthier. Time-weighted averages weight by duration, so the heavy request contributes proportionally to how long it actually occupied the system.

  2. Whole-run averages dilute by idle gaps. LLM inference has two distinct phases per request: prefill (compute-bound, brief, processes the input) and decode (memory-bound, long, generates one token at a time). At any instant most in-flight requests are in decode; prefill windows are brief and sparse. A whole-window average of “prefill throughput” reports a number diluted by all the decode-only time and is much smaller than the per-prefill-burst intensity the hardware actually delivers.

  3. Coordinated omission. Under a saturating load generator, requests pile up in the AIPerf credit queue before being dispatched. The server-side timing (request_start_ns to end_ns) excludes that queue wait, so a naive latency understates what an actual user — who issued the request at credit_issued_ns — would have observed. AIPerf addresses this with effective_latency, which charges the queue wait to the request.

The Effective and Active metric groups, together with effective_latency, are AIPerf’s responses to these three traps.

Effective metrics: full-window time-weighted views

AIPerf’s analyzer builds a step function over each quantity of interest (concurrency, decode throughput, prefill throughput, total throughput, tokens-in-flight) using a vectorized sweep-line algorithm on the per-request timestamp columns. The step function holds value v_i from event t_i to event t_{i+1}. An Effective metric is then the time-weighted average

avg = Σ (v_i × Δt_i) / (run_end − run_start)

Percentiles (p50, p90, p95, p99) are also duration-weighted: AIPerf sorts the (v_i, Δt_i) pairs by value, takes a cumulative duration fraction, and reads off the quantile. A “p99 of 920 tok/s” therefore means “99% of the run-window time, decode throughput was at or below 920 tok/s” — not “99% of the records had throughput at or below 920 tok/s”.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 0 5000 10000 15000 20000 25000 30000 35000 time (s) prefill throughput (tok/s) Effective: time-weighted average over the full run window

The full set of Effective metrics emitted today:

MetricUnitWhat it represents
effective_concurrencyrequestsTime-weighted in-flight request count over the run window
effective_decode_concurrencyrequestsSame, restricted to the decode phase [generation_start, end]
effective_prefill_concurrencyrequestsSame, restricted to the prefill phase [start, generation_start]
effective_decode_throughputtokens/secΣ per-request decode rates, time-weighted over the run
effective_prefill_throughputtokens/secΣ per-request prefill rates, time-weighted over the run
effective_total_throughputtokens/secPrefill + decode combined
effective_decode_throughput_per_usertokens/sec/userDecode throughput divided by decode concurrency
effective_prefill_throughput_per_usertokens/sec/userPrefill throughput divided by prefill concurrency
tokens_in_flighttokensKV-cache occupancy proxy: tokens currently being processed

Active metrics: phase-restricted views

Active variants use the same sweep-line rate curve, but the integration window is restricted to segments where the relevant phase mask is strictly positive. For active_prefill_throughput the mask is prefill_concurrency > 0; for active_decode_throughput it is decode_concurrency > 0. Time when no request is in that phase contributes zero duration to the denominator, so the average reflects intensity during the phase rather than diluted by gaps.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 0 5000 10000 15000 20000 25000 30000 35000 time (s) prefill throughput (tok/s) Active: average restricted to segments where prefill concurrency > 0

In the bar diagram, only the non-zero spikes contribute to both numerator and denominator — the zero-valued bands are excluded.

The Active metrics emitted today:

MetricMask usedUnit
active_decode_throughputdecode_concurrency > 0tokens/sec
active_prefill_throughputprefill_concurrency > 0tokens/sec
active_total_throughputoverall concurrency > 0tokens/sec
active_decode_throughput_per_userdecode_concurrency > 0tokens/sec/user
active_prefill_throughput_per_userprefill_concurrency > 0tokens/sec/user

Worked example (real AIPerf run)

Run: aiperf profile -m mock-model --streaming --concurrency 16 --request-count 200 --synthetic-input-tokens-mean 200 --output-tokens-mean 100 against the in-repo mock server with TTFT=100 ms, ITL=20 ms. Benchmark duration: 27.06 s.

Selected rows from the end-of-run console tables:

Metricavgp50p90p99max
Effective Decode Concurrency (req)14.6316.0016.0016.0016.00
Effective Prefill Concurrency (req)0.750.000.0016.0016.00
Effective Decode Throughput (tok/s)726.33792.31830.99859.22921.87
Effective Prefill Throughput (tok/s)1,477.970.000.0031,786.1531,851.62
Active Decode Throughput (tok/s)754.96795.03831.04859.40921.87
Active Prefill Throughput (tok/s)28,140.8131,746.3431,791.3131,851.6231,851.62

Two observations:

  • Decode is almost always active, so Effective Decode and Active Decode track each other (727 vs 755 tok/s). Decode dominates the run window — the mean decode concurrency of 14.63 is ≈ 91% of the 16 offered slots (14.63/16.0), so at almost every instant nearly all in-flight requests are in decode.
  • Prefill is sparse, so Effective and Active disagree by ~19×. effective_prefill_concurrency averages 0.75 across the whole window — prefill is in flight only a small fraction of the time. When you ask “what is the prefill throughput of this system?”, Active (28k tok/s) is the answer about hardware capability; Effective (1.5k tok/s) is the answer about how much prefill work the workload demanded on average. Both are correct; they answer different questions.

The Effective row’s p50 = 0 for prefill is not a bug — it correctly reports that for more than half of the run window, no request was in prefill, so the time-weighted median of the prefill-throughput step function is exactly zero.

effective_latency: the coordinated-omission-aware latency

effective_latency is grouped under MetricConsoleGroup.EFFECTIVE even though, unlike the sweep-line metrics, it is a per-record metric. The definition is:

effective_latency = end_ns − credit_issued_ns

Compare to the classical request_latency = end_ns − start_ns. The difference, start_ns − credit_issued_ns, is the time the request spent waiting in AIPerf’s credit queue — invisible to the server but real to the user.

This metric is only emitted when at least one record carries a credit_issued_ns timestamp. Every AIPerf load mode dispatches requests through the credit issuer, which stamps each credit with issued_at_ns — the worker then records it as credit_issued_ns — so effective_latency is available for closed-loop (--concurrency) and open-loop (--request-rate, trace replay) runs alike. It is suppressed only when no record in the run carries a credit_issued_ns timestamp. Comparing effective_latency against request_latency tells you how much of perceived latency is queue-induced (load-generator backpressure) versus server-induced (the model itself):

  • If they are essentially equal — as in the worked example above, where both averaged ~2,081 ms — your load is not saturating; queue wait is negligible.
  • If effective_latency is materially larger than request_latency, you have crossed into a saturating regime. The “real” tail latency users observe is the effective_latency distribution, not the server-side one.

This is AIPerf’s answer to the coordinated-omission problem made famous by Gil Tene: a naïve benchmark that omits queue wait under-reports user-perceived latency precisely when the system is most stressed.

Choosing a metric

You want to answer…Cite
”What sustained decode throughput should I plan for at this concurrency level?”effective_decode_throughput
”What was the peak decode throughput the GPU achieved while decoding?”active_decode_throughput (close to effective_decode_throughput when decode is rarely idle)
“What is this server’s prefill capability under bursty arrival?”active_prefill_throughput — Effective will dilute it by decode-only time
”How saturated was my load generator? Did the credit queue back up?”Compare effective_latency against request_latency
”What latency does a user actually perceive under this load?”effective_latency (when emitted)
“What is the KV-cache pressure during this run?”tokens_in_flight

Reading the console output

In the standard end-of-run output, AIPerf renders one table per non-empty MetricConsoleGroup in the order EFFECTIVE, ACTIVE, USAGE, CACHE, PREDICTION, AUDIO, REASONING, DEFAULT. A vanilla LLM run typically shows three: NVIDIA AIPerf | LLM Metrics: Effective, NVIDIA AIPerf | LLM Metrics: Active, and the legacy/default NVIDIA AIPerf | LLM Metrics table containing record-level distributions (TTFT, ITL, request latency, OSL, ISL). Endpoint types that emit usage, cache, prediction, audio, or reasoning tokens add intermediate tables. The grouping is driven by the console_group class attribute on each metric.

References

  • Definitions and formulas for the record-level metrics referenced here (TTFT, ITL, request latency, ISL/OSL): docs/metrics-reference.md. The Effective/Active families themselves are defined only in this brief.
  • Coordinated omission background: Gil Tene, “How NOT to Measure Latency” (Strange Loop 2015)