Skip to navigation

AIPerf Server Metrics Parquet Export Schema

View as Markdown

Schema reference for the server_metrics_export.parquet file. Optimized for SQL analytics with DuckDB, pandas, and Polars.

Overview

The Parquet export provides raw time-series data with cumulative delta calculations applied at each timestamp. Uses a normalized schema where histogram buckets are separate rows (not wide columns), producing ~50% smaller files.

Enable Parquet Export

aiperf profile --model MODEL ... --server-metrics-formats json csv parquet

Delta Calculations

All values are deltas from a reference point (last sample before profiling period):

Metric TypeValue Semantics
GaugeRaw value at timestamp (no delta)
CounterCumulative delta from reference (value[t] - value[ref])
HistogramCumulative deltas for sum, count, and each bucket_count

Negative deltas (counter resets) are clamped to 0.


Schema Definition

Fixed Columns

ColumnTypeNullableDescription
endpoint_urlstringNoPrometheus endpoint URL (e.g., http://localhost:8000/metrics)
metric_namestringNoMetric name (e.g., vllm:kv_cache_usage_perc)
metric_typestringNogauge, counter, histogram, or unknown (Prometheus # TYPE foo untyped)
unitstringYesInferred unit (seconds, tokens, requests, ratio, etc.)
descriptionstringYesMetric HELP text from Prometheus
timestamp_nsint64NoCollection timestamp in nanoseconds since epoch

Value Columns

ColumnTypeNullableUsed ByDescription
valuefloat64YesGauge, CounterMetric value (raw for gauge, delta for counter)
sumfloat64YesHistogramCumulative sum delta from reference
countfloat64YesHistogramCumulative count delta from reference
bucket_lestringYesHistogramBucket upper bound (e.g., 0.1, +Inf)
bucket_countfloat64YesHistogramCumulative bucket count delta (observations <= bucket_le)

Dynamic Label Columns

Prometheus labels become individual columns (alphabetically sorted):

ColumnTypeNullableDescription
enginestringYesvLLM engine ID
engine_typestringYesEngine type (trtllm, unified, prefill, decode)
finished_reasonstringYesRequest completion reason
reasonstringYesvLLM waiting reason or Triton failure reason
sleep_statestringYesvLLM engine sleep state
sourcestringYesvLLM prompt-token source
positionstringYesvLLM speculative-decoding draft position
transfer_typestringYesvLLM KV offload transfer type
model_namestringYesModel identifier
dynamo_componentstringYesDynamo worker component
worker_idstringYesDynamo worker identifier
worker_typestringYesDynamo worker type (prefill, decode, etc.)
router_idstringYesDynamo router identifier
operationstringYesDynamo operation name
migration_typestringYesDynamo request migration type
event_typestringYesDynamo KV publisher event type
workerstringYesTokio worker index
poolstringYesDynamo KVBM logical pool name
instance_idstringYesDynamo KVBM external instance label
tp_rankstringYesTensor parallel rank
pp_rankstringYesPipeline parallel rank
moe_ep_rankstringYesSGLang MoE expert-parallel rank
dp_rankstringYesSGLang data-parallel rank
prioritystringYesSGLang priority-scheduling value
stagestringYesSGLang processing stage
modestringYesSGLang token/CUDA graph mode
categorystringYesSGLang forward execution category
cache_sourcestringYesSGLang cache source (device, host, storage_*, total)
num_prefill_ranksstringYesSGLang DP cooperation prefill-rank count
input_estimationstringYesSGLang prefill-delayer input estimate
output_allowstringYesSGLang prefill-delayer output allowance
output_reasonstringYesSGLang prefill-delayer output reason
actual_executionstringYesSGLang prefill-delayer execution outcome
forward_modestringYesSGLang expert-parallel forward mode
layerstringYesSGLang model layer
request_typestringYesTriton/TensorRT-LLM backend request type
model_namespacestringYesTriton model namespace
gpu_uuidstringYesTriton GPU UUID
_custom_tagstringYesTriton model tag labels (actual column name uses the configured tag name prefixed with _)
memory_typestringYesTensorRT-LLM backend memory type
kv_cache_block_typestringYesTensorRT-LLM backend KV-cache block type
disaggregated_serving_typestringYesTensorRT-LLM backend disaggregated-serving metric type
versionstringYesTriton model version
(others)stringYesAny additional Prometheus labels

Label columns vary by endpoint/model. Use union_by_name=true for cross-file queries.

Note: Prometheus labels that conflict with reserved column names (endpoint_url, metric_name, metric_type, unit, description, timestamp_ns, value, sum, count, bucket_le, bucket_count) are silently excluded.


Row Structure by Metric Type

Column order: fixed columns → label columns (alphabetically) → value columns.

Gauge/Counter: One Row per Timestamp

endpoint_url | metric_name | metric_type | unit | description |timestamp_ns | model_name | value | sum | count | bucket_le | bucket_count
-------------|--------------------------|-------------|-------|-------------|---------------------|--------------|-------|------|-------|-----------|-------------
http://... | vllm:kv_cache_usage_perc | gauge | ratio | KV-cache... | 1765793061967310848 | Qwen/Qwen3-0.6B | 0.72 | null | null | null | null
http://... | vllm:request_success | counter | null | Count of... | 1765793061967310848 | Qwen/Qwen3-0.6B | 150.0 | null | null | null | null

Histogram: N Rows per Timestamp (One per Bucket)

endpoint_url | metric_name | metric_type | unit | description | timestamp_ns | model_name | value | sum | count | bucket_le | bucket_count
-------------|----------------------------------|-------------|---------|--------------|---------------------|-----------------|-------|--------|-------|-----------|-------------
http://... | vllm:e2e_request_latency_seconds | histogram | seconds | Histogram... | 1765793061967310848 | Qwen/Qwen3-0.6B | null | 259.87 | 19.0 | 0.3 | 0.0
http://... | vllm:e2e_request_latency_seconds | histogram | seconds | Histogram... | 1765793061967310848 | Qwen/Qwen3-0.6B | null | 259.87 | 19.0 | 1.0 | 1.0
http://... | vllm:e2e_request_latency_seconds | histogram | seconds | Histogram... | 1765793061967310848 | Qwen/Qwen3-0.6B | null | 259.87 | 19.0 | 5.0 | 3.0
http://... | vllm:e2e_request_latency_seconds | histogram | seconds | Histogram... | 1765793061967310848 | Qwen/Qwen3-0.6B | null | 259.87 | 19.0 | +Inf | 19.0

File Metadata

Parquet file metadata (accessible via pq.read_metadata()) includes:

KeyDescription
aiperf.schema_versionSchema version (1.0)
aiperf.versionAIPerf version
aiperf.benchmark_idUnique benchmark UUID
aiperf.exporterExporter class name (ServerMetricsParquetExporter)
aiperf.export_timestamp_utcExport timestamp (ISO 8601)
aiperf.time_filter_start_nsProfiling period start (nanoseconds)
aiperf.time_filter_end_nsProfiling period end (nanoseconds)
aiperf.profiling_duration_nsProfiling duration (nanoseconds)
aiperf.profiling_duration_secondsProfiling duration (seconds)
aiperf.endpoint_urlsJSON array of endpoint URLs
aiperf.endpoint_countNumber of endpoints
aiperf.label_columnsJSON array of label column names
aiperf.label_countNumber of label columns
aiperf.metric_countTotal unique metrics
aiperf.metric_type_countsJSON object: {"gauge": N, "counter": N, "histogram": N, "unknown": N}
aiperf.model_namesJSON array of model names
aiperf.concurrencyBenchmark concurrency setting
aiperf.request_rateBenchmark request rate (if set)
aiperf.input_configFull user configuration (JSON)
aiperf.hostnameCollection host
aiperf.python_versionPython version
aiperf.pyarrow_versionPyArrow version
aiperf.schema_noteCross-file query hint

Compression: Snappy (good compression ratio with fast decompression)


Example Queries

DuckDB

-- Time-series for a specific metric
SELECT timestamp_ns, value
FROM 'server_metrics_export.parquet'
WHERE metric_name = 'vllm:kv_cache_usage_perc'
ORDER BY timestamp_ns;
-- Filter by label
SELECT timestamp_ns, value
FROM 'server_metrics_export.parquet'
WHERE metric_name = 'vllm:request_success'
AND model_name = 'Qwen/Qwen3-0.6B'
ORDER BY timestamp_ns;
-- Histogram bucket distribution at final timestamp
SELECT bucket_le, bucket_count
FROM 'server_metrics_export.parquet'
WHERE metric_name = 'vllm:e2e_request_latency_seconds'
AND timestamp_ns = (SELECT MAX(timestamp_ns) FROM 'server_metrics_export.parquet'
WHERE metric_name = 'vllm:e2e_request_latency_seconds')
ORDER BY CAST(REPLACE(bucket_le, '+Inf', '999999') AS DOUBLE);
-- Aggregate across multiple runs (handles schema differences)
SELECT metric_name, AVG(value) as avg_value
FROM read_parquet('artifacts/*/server_metrics_export.parquet', union_by_name=true)
WHERE metric_type = 'gauge'
GROUP BY metric_name;
-- Compare endpoints
SELECT endpoint_url, metric_name, AVG(value) as avg_value
FROM 'server_metrics_export.parquet'
WHERE metric_type = 'gauge'
GROUP BY endpoint_url, metric_name;

pandas

import pandas as pd
df = pd.read_parquet('server_metrics_export.parquet')
# Filter to gauge metrics
gauges = df[df['metric_type'] == 'gauge']
# Time-series plot
kv_usage = df[df['metric_name'] == 'vllm:kv_cache_usage_perc']
kv_usage.plot(x='timestamp_ns', y='value', title='KV Cache Usage')
# Pivot histogram buckets
hist = df[df['metric_name'] == 'vllm:e2e_request_latency_seconds']
pivot = hist.pivot(index='timestamp_ns', columns='bucket_le', values='bucket_count')

Polars

import polars as pl
df = pl.read_parquet('server_metrics_export.parquet')
# Filter and aggregate
result = (
df.filter(pl.col('metric_type') == 'gauge')
.group_by('metric_name')
.agg([
pl.col('value').mean().alias('avg'),
pl.col('value').max().alias('max'),
])
)
# Lazy scan for large files
lazy = pl.scan_parquet('artifacts/*/server_metrics_export.parquet')
result = lazy.filter(pl.col('metric_name') == 'vllm:kv_cache_usage_perc').collect()

Reading Metadata

import pyarrow.parquet as pq
import json
metadata = pq.read_metadata('server_metrics_export.parquet')
schema_metadata = metadata.schema.to_arrow_schema().metadata
# Access specific fields
benchmark_id = schema_metadata[b'aiperf.benchmark_id'].decode()
config = json.loads(schema_metadata[b'aiperf.input_config'])
label_columns = json.loads(schema_metadata[b'aiperf.label_columns'])

Best Practices

Cross-File Analysis

Label columns vary by endpoint and model. Always use union_by_name:

-- DuckDB
SELECT * FROM read_parquet('run_*/server_metrics_export.parquet', union_by_name=true);
# pandas
import pandas as pd
from pathlib import Path
dfs = [pd.read_parquet(p) for p in Path('.').glob('run_*/server_metrics_export.parquet')]
combined = pd.concat(dfs, ignore_index=True)

Histogram Percentile Estimation

Reconstruct percentiles from bucket data. Note that bucket_count values are cumulative (each bucket includes all observations with value <= bucket_le), matching Prometheus histogram semantics:

import numpy as np
def estimate_percentile(bucket_les, bucket_counts, percentile):
"""Estimate percentile from histogram buckets using linear interpolation."""
# Convert bucket_le strings to floats (handle +Inf)
bounds = [float(b) if b != '+Inf' else np.inf for b in bucket_les]
counts = np.array(bucket_counts)
total = counts[-1] # +Inf bucket has cumulative total
target = total * (percentile / 100)
for i, (le, count) in enumerate(zip(bounds, counts)):
if count >= target:
if i == 0:
return le
prev_le = bounds[i-1] if i > 0 else 0
prev_count = counts[i-1] if i > 0 else 0
# Linear interpolation within bucket
fraction = (target - prev_count) / (count - prev_count) if count > prev_count else 0
return prev_le + fraction * (le - prev_le)
return bounds[-2] # Return last finite bound

Memory-Efficient Processing

For large files, use lazy evaluation:

# Polars lazy scan
import polars as pl
df = pl.scan_parquet('server_metrics_export.parquet') \
.filter(pl.col('metric_name') == 'vllm:kv_cache_usage_perc') \
.collect()
# DuckDB direct query (doesn't load entire file)
import duckdb
result = duckdb.query("""
SELECT AVG(value) FROM 'server_metrics_export.parquet'
WHERE metric_name = 'vllm:kv_cache_usage_perc'
""").fetchone()

Schema Version History

VersionChanges
1.0Initial schema with normalized histogram buckets

For aggregated statistics, see JSON Schema. For metric definitions, see Server Metrics Reference. For usage examples, see the Server Metrics Tutorial.