Observability#
UCM exports metrics through the vLLM /metrics endpoint. By default, the
built-in metrics config in ucm/default_metrics_config.py registers the full
metric set. If metrics_config_path is provided, that file becomes the
enable-list instead. Metrics are accumulated inside UCM and fanned out to the
enabled Python-side consumers.
How Metrics Flow#
The built-in default config, or a user-provided
metrics_configs.yaml, defines counters, gauges, and histograms.The Python metrics dispatcher drains the C++ metrics snapshot once and fans it out to the enabled
multiprocandvllm_connectorconsumers.multiproccreatesprometheus_clientmetrics withmodel_nameandworker_idlabels.vllm_connectorcreates vLLM KV connector metrics withmodel_name,engine, andworker_ranklabels.Histogram bucket boundaries are taken from the Python Prometheus histogram and registered into the C++ metrics library.
UCM code calls
UpdateStats()on the hot path.The C++ metrics library records counter, gauge, and histogram bucket deltas in per-thread double buffers.
The dispatcher applies deltas to each enabled Python consumer without one consumer clearing the other’s accumulated snapshot.
vLLM exposes the resulting cumulative Prometheus series through
/metrics.
Histograms are bucketed at update time. UCM no longer stores raw histogram
sample vectors, so there is no histogram_max_length setting and no histogram
sample dropping caused by a vector length cap. The +Inf bucket is added
automatically when the metric is registered.
Quick Start#
1. Configure UCM Metrics#
Set the Prometheus multiprocess directory before starting vLLM:
export PROMETHEUS_MULTIPROC_DIR=/vllm-workspace
In the UCM config file used by vLLM, metrics are enabled by default. You can set the switch explicitly:
enable_metrics: true
To disable all UCM metrics:
enable_metrics: false
To customize which metrics are registered, set metrics_config_path to the
metrics configuration file you want to use. When this path is omitted, UCM uses
the built-in default config:
metrics_config_path: "/vllm-workspace/unified-cache-management/examples/metrics/metrics_configs.yaml"
Then start vLLM with the UCM connector:
export CUDA_VISIBLE_DEVICES=0
vllm serve /home/models/Qwen2.5-14B-Instruct \
--max-model-len 5000 \
--tensor-parallel-size 1 \
--gpu_memory_utilization 0.87 \
--trust-remote-code \
--disable-log-requests \
--no-enable-prefix-caching \
--enforce-eager \
--max-num-batched-tokens 40000 \
--max-num-seqs 10 \
--host 0.0.0.0 \
--port 8000 \
--kv-transfer-config \
'{
"kv_connector": "UCMConnector",
"kv_connector_module_path": "ucm.integration.vllm.ucm_connector",
"kv_role": "kv_both",
"kv_connector_extra_config": {
"UCM_CONFIG_FILE": "/vllm-workspace/unified-cache-management/examples/ucm_config.yaml"
}
}'
Run a benchmark if you want traffic for the metrics:
vllm bench serve \
--backend vllm \
--model /home/models/Qwen2.5-14B-Instruct \
--host 127.0.0.1 \
--port 8000 \
--dataset-name random \
--num-prompts 20 \
--random-input-len 200 \
--random-output-len 10 \
--request-rate 1 \
--ignore-eos
Check that UCM vLLM connector metrics are present:
curl http://<vllm-worker-ip>:8000/metrics | grep 'ucm:'
If the multiproc consumer is enabled, Prometheus multiprocess .db files
should also appear in $PROMETHEUS_MULTIPROC_DIR.
2. Start Prometheus and Grafana#
Create docker-compose.yaml:
version: "3"
services:
prometheus:
image: prom/prometheus:latest
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "9090:9090"
volumes:
- ${PWD}/prometheus.yaml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
depends_on:
- prometheus
ports:
- "3000:3000"
Create prometheus.yaml:
global:
scrape_interval: 5s
evaluation_interval: 30s
scrape_configs:
- job_name: vllm
static_configs:
- targets:
- "host.docker.internal:8000"
Make sure the target port matches the vLLM service port. Then start the stack:
docker compose up
3. Import Grafana Dashboards#
Open http://<your-host>:3000, add a Prometheus data source pointing to
http://prometheus:9090, then import the dashboard JSONs you need:
File |
Use case |
|---|---|
|
Connector-level activity: hit rate, request/block sizes, end-to-end load/save durations and speeds. |
|
Cache Store and Posix Store diagnosis: task breakdowns, queue wait, transfer duration, bandwidth, backend load ratio. |
|
|
|
vLLM service-side request latency, token throughput, scheduler state, and cache state. |
The dashboards share the ucm Grafana tag. After importing them, the dashboard
header contains an “Other UCM dashboards” dropdown that links between UCM
dashboards while preserving the time range and model_name value.
Dashboard Job, View, and Worker Selectors#
Each dashboard has a job selector. It defaults to All and uses regex
matching, so dashboards also work for metrics that do not carry a job label.
The UCM dashboards also have a View selector and a worker_rank selector:
Aggregated: default service-level view. Worker labels are collapsed.
Per Worker: split panels by
worker_rankfor worker-specific diagnosis.worker_rank: defaults to All. Select a specific worker rank to filter all UCM panels to that worker only.
Heatmap panels and panels grouped by another dimension may ignore the View
selector because their grouping is already defined by the panel. They still use
the worker_rank filter.
Metrics Configuration#
Metrics are configured in examples/metrics/metrics_configs.yaml:
log_interval: 5
# multiproc_dir: "/vllm-workspace"
# multiproc_prefix: "ucm_multiproc:"
vllm_connector_prefix: "ucm:"
consumers:
# multiproc: true
vllm_connector: true
counter:
- name: "cache_load_bytes_total"
documentation: "Total bytes loaded through the Cache stage"
gauge:
- name: "cache_lookup_hit_rate"
documentation: "Instantaneous Cache stage hit rate from the most recent lookup call"
multiprocess_mode: "livemostrecent"
histogram:
- name: "cache_load_duration_ms"
documentation: "End-to-end Cache stage load task duration (ms)"
buckets: [0.1, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
Metric names are exported per consumer. For example, cache_load_duration_ms
is exported as ucm:cache_load_duration_ms by the default vllm_connector
consumer. If multiproc is also enabled, use a separate prefix such as
ucm_multiproc: so both consumers do not register the same Prometheus metric.
Prometheus also exports histogram helper series such as _bucket, _sum,
and _count.
Counter values are increments. Gauge values replace the current value. Histogram values are observations that are immediately assigned to configured buckets in the C++ metrics library.
Available Metrics#
The default metrics configuration contains the following UCM metric names. The
table uses the default vllm_connector_prefix. UCM duration metrics are
exported in milliseconds.
Counters#
Metric |
Description |
|---|---|
|
Number of lookup hits served by the Cache stage. |
|
Number of lookup misses at the Cache stage. |
|
Total blocks loaded by the Cache stage. |
|
Total blocks dumped by the Cache stage. |
|
Total shards dispatched by Cache load. |
|
Shards that descended to the backend on load. |
|
Total shards dispatched by Cache dump. |
|
Shards actually pushed to backend on dump. |
|
Total bytes loaded through the Cache stage. |
|
Total bytes dumped through the Cache stage. |
|
Total bytes transferred from Posix storage to host buffer. |
|
Total bytes transferred from host buffer to Posix storage. |
|
Total bytes loaded through the UCM connector. |
|
Total bytes saved through the UCM connector. |
Gauges#
Metric |
Description |
|---|---|
|
Instantaneous Cache stage hit rate from the most recent lookup call. |
Histograms#
Metric |
Description |
|---|---|
|
Number of requests loaded from UCM. |
|
Number of blocks loaded from UCM. |
|
Time to load from UCM in milliseconds. |
|
Speed of loading from UCM in GB/s. |
|
Number of requests saved to UCM. |
|
Number of blocks saved to UCM. |
|
Time from UCM connector |
|
Time spent blocked while confirming async UCM connector dump completion in milliseconds. |
|
Hit rates of UCM lookup requests. |
|
Cache buffer lookup wall-clock time. |
|
Backend lookup wall-clock time when descending due to no buffer or buffer miss. |
|
End-to-end Cache stage load task duration in milliseconds. |
|
End-to-end Cache stage dump task duration in milliseconds. |
|
Cache stage effective load throughput in GB/s over the whole task lifetime, including queue/backend waits. Not a DMA bandwidth (see |
|
Cache stage effective dump throughput in GB/s over the whole task lifetime, including queue and compute-event waits. Not a DMA bandwidth (see |
|
Time a Cache load task spent queued before dispatch worker pickup. |
|
Time a Cache dump task spent queued before dispatch worker pickup. |
|
Cache load backend submit duration: buffer allocation plus backend load submission. |
|
Cache load per-shard |
|
Cache load per-shard H2D async submit CPU cost. Submission only, not the transfer. |
|
Cache load residual H2D stream drain after the last shard submit. Large values mean H2D copy is the bottleneck. |
|
Cache load pure H2D copy bandwidth, directly comparable to memcpy microbenchmarks. |
|
Cache dump mk_buf phase duration (buffer allocation/reuse plus D2H async submit). |
|
Cache dump wait for the prerequisite compute event before D2H can start. Large values mean dump is compute-gated. |
|
Cache dump pure D2H copy drain, compute-event wait excluded. |
|
Cache dump pure D2H copy bandwidth, directly comparable to memcpy microbenchmarks. |
|
Cache dump synchronous backend submit duration. |
|
Cache dump wait for the lower tier to finish writing. Large values mean storage write is the bottleneck. |
|
End-to-end Posix load task duration. |
|
End-to-end Posix dump task duration. |
|
Posix stage read bandwidth per task in GB/s. |
|
Posix stage write bandwidth per task in GB/s. |
|
Time a Posix load task spent queued before first worker pickup. |
|
Time a Posix dump task spent queued before first worker pickup. |
|
Layerwise batch wall-clock time from |
|
Layerwise load-only batch wall-clock time. |
|
Layerwise save-only batch wall-clock time. |
|
Layerwise load-and-save batch wall-clock time. |
|
Layerwise batch wall-clock time with neither load nor save work. |
|
Total |
|
Total |
|
|
|
|
|
Time |
|
Number of per-request load tasks awaited in a single layer wait. |
|
Interval between consecutive |
|
Time to submit next layer’s load tasks inside |
|
Time to submit first layer load tasks during |
|
Number of requests whose first-layer load was submitted in |
|
Time to submit one layer’s dump task in |
|
Legacy metric; LayerWise no longer waits for dump completion in |