Conversation
|
Why do we need to expose the number of CPU blocks? |
GPU is exposed - num_gpu_blocks is a label on vllm:cache_config_info. For CPU there is only usage %, so this fills that gap. I kept it as a gauge in kv_offload_cpu_* next to usage, rather than a cache_config_info label like GPU, since the offload buffer belongs to the connector rather than the engine's cache config IMO. |
I see. Then let's follow a similar pattern: |
|
Agree that adding |
Yup, agree on this. Besides I understand that this has the similar fuctionality as |
5a7a897 to
e7c15f0
Compare
e7c15f0 to
2debb90
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
orozery
left a comment
There was a problem hiding this comment.
I suggest we avoid the changes outside the offloading connector / kv_offload.
I guess this means we observe on the first scheduler step instead of on startup.
prometheus_client.Gauge defaults to multiprocess_mode="all", which in multiprocess mode (api_server_count > 1) exports one series per pid. The offloading gauges -- vllm:kv_offload_cpu_cache_usage_perc and its read/write variants -- are point-in-time values for a single engine that every API-server process records from whatever snapshot it happened to see, so /metrics ends up with one duplicate row per process and a dashboard summing by engine can exceed 1.0. Every other point-in-time gauge in PrometheusStatLogger already sets "mostrecent" (vllm:num_requests_running, vllm:engine_sleep_state and the GPU-tier analogue vllm:kv_cache_usage_perc); the offloading gauges were the only ones left on the default. Default OffloadingGaugeMetadata to multiprocess_mode="mostrecent" and pass it through when the gauge is created. Counters and histograms are per-process cumulative and must stay summed, so they keep the default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Yan Burman <yanburman@users.noreply.github.com>
The CPU KV OffloadingConnector exposed only the usage fraction, with no way to observe the tier's static configuration or capacity. Add vllm:kv_offload_cpu_config_info, a cache_config_info-style Info metric whose value is always 1 and whose labels carry the static config: num_blocks, blocks_per_chunk, kv_bytes_per_chunk, cpu_page_size_per_worker and eviction_policy (num_gpu_blocks-equivalent capacity = num_blocks * blocks_per_chunk). This mirrors how the GPU tier's config, including num_gpu_blocks, is exposed via vllm:cache_config_info. Without it a mis-sized tier is invisible: when cpu_bytes_to_use is smaller than one aligned chunk, num_blocks becomes 0 and the usage gauges report 0.0 forever -- indistinguishable from a healthy idle tier. Emitting the config at startup surfaces that before any traffic is routed to the engine. The metric is emitted once at engine startup (before any request) and deliberately NOT on the per-interval connector-stats path -- the Prometheus gauge persists once set, and a capacity metric that only appears after the first scheduler step cannot be used to validate a deployment before it serves traffic. A new generic KVConnectorBase_V1.get_config_info() hook exposes a connector's static config, the values ride the EngineCoreReadyResponse handshake into the frontend (stored on KVTransferConfig, alongside the num_gpu_blocks/kv_cache_size_tokens that already cross it for cache_config_info), and PrometheusStatLogger emits them in log_engine_initialized. MultiConnector aggregates its nested children's config (and its prom fans the emission to each child), and TieringOffloadingManager surfaces its CPU primary tier's config -- so an OffloadingConnector standalone, nested in a MultiConnector (PD), or as a tiering primary tier all emit at startup. The gauge inherits multiprocess_mode="mostrecent" from OffloadingGaugeMetadata, so it stays one series per engine under data parallelism with api_server_count > 1. Covered by manager, spec-wiring, Prometheus-emission and MultiConnector-aggregation unit tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Yan Burman <yanburman@users.noreply.github.com>
2debb90 to
811a511
Compare
The outside change is not introducing a new mechanism. It rides on existing pattern that is in use by num_gpu_blocks and is quite small if you look only at production code. Making it only populate on first request makes monitoring a bit awkward and diverges from current gpu blocks implementation |
@NickLucche @ivanium your thoughts on this? TLDR adding a dedicated connector API for exposing runtime level config: |
|
Wonder if we could expose the CPU offload capacity directly in tokens, not only as
llm-d router #2292 is a concrete consumer. It needs token capacity to size its approximate per-endpoint cache, and already reads SGLang's Could we add a safely computed I have a small prototype with tests for one group, multiple equal-span groups, and multiple differing-span groups. Would you prefer folding it into this PR or handling it as a follow-up after this lands? |
|
I think this can be split into two independent pieces:
I opened #51615 as a concrete example of the first piece. It adds It emits only for a single KV-cache group. With multiple groups, the CPU pool stores group-specific This draft is not intended to replace the full static configuration proposed here. It demonstrates how the token-capacity piece required by llm-d router #2292 can be unblocked while the generic startup/configuration plumbing is reviewed separately. The implementation can be folded into this PR if preferred; otherwise the narrow metric can land first and the broader |
|
This pull request has merge conflicts that must be resolved before it can be |
The native CPU
OffloadingConnectorreported usage fractions but exposed no metric for the offload tier's static configuration or capacity. Addvllm:kv_offload_cpu_config_info, acache_config_info-style Info gauge (value always 1; config carried in the labels) reportingnum_blocks,blocks_per_chunk(num_gpu_blocks-equivalent capacity =num_blocks * blocks_per_chunk; multiply by the cacheblock_sizefor a token count),kv_bytes_per_chunk,cpu_page_size_per_workerandeviction_policy. Emit it once at engine startup via a newKVConnectorBase_V1.get_config_info()hook and theEngineCoreReadyResponsehandshake, covering standalone,MultiConnector-nested (PD) and tiering-primary connectors, and cover it with manager and startup-emission unit tests.Purpose
The native CPU KV
OffloadingConnectorreports usage-fraction gauges(
vllm:kv_offload_cpu_cache_usage_perc,vllm:kv_offload_cpu_cache_write_usage_perc,vllm:kv_offload_cpu_cache_read_usage_perc) but exposes no metric for the offloadtier's static configuration or capacity, so none of it is observable on
/metrics.This PR adds an Info gauge,
vllm:kv_offload_cpu_config_info, whose value is always 1and whose labels carry the tier's static config:
num_blocks(offload capacity inoffload blocks),
blocks_per_chunk(GPU blocks per offload block — num_gpu_blocksequivalents =
num_blocks * blocks_per_chunk; multiply by the cacheblock_sizefora token count),
kv_bytes_per_chunk,cpu_page_size_per_workerandeviction_policy.This mirrors how the GPU tier's config, including
num_gpu_blocks, is exposed viavllm:cache_config_info.The metric is emitted once at engine startup (before any request) and deliberately
not on the per-interval connector-stats path — re-serializing a constant gauge over
IPC every interval is wasted work, and the Prometheus gauge persists once set. A new
generic
KVConnectorBase_V1.get_config_info()hook exposes a connector's staticconfig, the values ride the
EngineCoreReadyResponsehandshake into the frontend(stored on
KVTransferConfig), andPrometheusStatLoggeremits them inlog_engine_initialized.MultiConnectoraggregates its nested children's config(and its prom fans the emission to each child), and
TieringOffloadingManagersurfaces its CPU primary tier's config — so an
OffloadingConnectorstandalone,nested in a
MultiConnector(PD), or as a tiering primary tier all emit at startup.Offloading gauges default to
multiprocess_mode="mostrecent", so the static gauge(and the existing per-engine usage gauges) collapse to one series per engine under
data-parallel +
api_server_count > 1instead of one duplicate row per API-serverprocess; per-process transfer counters and histograms stay summed.
Scope: the native
OffloadingConnectoronly (standalone,MultiConnector-nested,tiering primary tier). No behavior change beyond the added metric and the
multiprocess_modecorrection on the existing offloading gauges.Test Plan
pytest tests/v1/kv_offload/cpu/test_manager.py tests/v1/kv_offload/test_factory.pytest_cpu_manager_reports_config_infoassertsget_config_info()returns{CPU_CONFIG_INFO: {...}}and that config_info is not placed on the per-intervalstats path.
test_cpu_config_info_wiring_matches_declared_labels,test_cpu_config_info_reflects_tensor_parallel_sizing,test_kv_offload_cpu_config_info_startup_emissionand..._via_multi_connectorcover label/dict anti-drift, TP/PP sizing, and end-to-end startup emission
(standalone and
MultiConnector-nested) with the gauge built asmostrecent.pre-commit run --files $(git diff --name-only origin/main...HEAD)--kv-offloading-size <GiB>and confirmvllm:kv_offload_cpu_config_infoappears on/metricsat startup with value 1 andlabels whose
num_blocks * blocks_per_chunkscales with the configured size.Test Result
Unit tests:
$ pytest tests/v1/kv_offload/cpu/test_manager.py
......................... [100%]
25 passed, 1 warning in 2.05s
$ pytest tests/v1/kv_offload/test_factory.py
.................................. [100%]
34 passed, 15 warnings in 12.38s
pre-commit (ruff check, ruff format, typos, mypy, SPDX headers, forbidden-imports,
config-docstring validation): all hooks passed.
Essential Elements of an Effective PR Description Checklist
docs/design/metrics.md, so this metric follows the existing (undocumented) pattern.