Prhub

#45737 [KV-Offloading] : Expose CPU cache usage metric

原始 PR 作者 varun-sundar-rabindranath 合并时间 2026-06-21 05:21 文件变更 4 提交数 4 评论 19 代码增减 +86 / -22

执行摘要

暴露 CPU KV 卸载缓存使用百分比指标

PR 正文明确说明目的:"Add vllm:kv_offload_cpu_cache_usage_perc metric to export CPU cache usage percent. The naming and the semantics is designed to match the gpu counter part vllm:kv_cache_usage_perc"。讨论中也提到需要监控 CPU 缓存容量是否饱和,以便运维决策。

值得精读,尤其是 get_stats 中的计算逻辑与阈值联动方式。设计讨论也值得关注:指标定义应该由 Spec 集中管理还是允许 Manager 自主注册?该 PR 选择了前者(通过 Spec 的 build_metric_definitions),并通过 CPUOffloadingMetrics 集中常量,是一个合理折衷。

讨论亮点

指标定义归属争议:orozery 最初质疑在 base.py 增加默认 build_metric_definitions 是否必要(已有 OffloadingSpec 的同名方法),varun 解释这是为了让 Manager 能注册自己特有的指标,但最终被要求移回 cpu/spec.py 通过基类机制注册。

指标名称与文档修订:orozery 建议将指标名定为 vllm:kv_offload_cpu_cache_usage_perc 以匹配 GPU 侧 vllm:kv_cache_usage_perc 的命名惯例,ronensc 表示同意。在文档字符串上,orozery 建议更精确的描述,varun 最初指出 get_stats 中计算包含非活动块会导致指标始终为 1.0,随后 orozery 承认这一点并明确应该只计数“当前已被钉住(pinned)”的块(即活跃传输中的块)。varun 随后将计算调整为扣除 _num_evictable_cache_blocks,使指标反映活跃请求占用的比例,并更新了文档。

测试归宿:orozery 反对将 CPU 指标测试放在通用 test_metrics.py 中,认为它属于 CPU 侧测试文件,最终测试代码被放置在 test_manager.py

实现拆解

  1. 定义指标常量集中类:在 vllm/v1/kv_offload/cpu/common.py 中新增 CPUOffloadingMetrics 类,统一存放两个指标名称常量 STORES_SKIPPEDCPU_CACHE_USAGE_PERC,替换原有分散的模块级常量。

  2. 注册指标定义:修改 vllm/v1/kv_offload/cpu/spec.py 中的 CPUOffloadingSpec.build_metric_definitions,使其总是返回 CPU_CACHE_USAGE_PERCOffloadingGaugeMetadata(无论 store_threshold 如何),而对于 STORES_SKIPPED 仍仅当 store_threshold >= 2 时添加 OffloadingCounterMetadata

  3. 实现指标计算与暴露:修改 vllm/v1/kv_offload/cpu/manager.pyget_stats 方法:移除了之前 store_threshold < 2 时直接返回 None 的早期退出;现在始终创建 OffloadingConnectorStats,通过 num_used = _num_allocated_blocks - len(_free_list) - _num_evictable_cache_blocks 计算已用块数,再除以总块数得到使用率(避免除零),设置 gauge;同时仅当 store_threshold >= 2 时执行计数器累加。

  4. 编写单元测试:在 tests/v1/kv_offload/cpu/test_manager.py 中添加 test_cpu_manager_reports_cache_usage_gauge 函数,通过 make_cpu_manager 构造不同状态的 manager 并调用 get_stats,验证使用率返回正确值(包括零容量、空、半满、全满、complete_store 释放等场景)。

文件 模块 状态 重要度
vllm/v1/kv_offload/cpu/common.py 指标定义 modified 6.22
vllm/v1/kv_offload/cpu/spec.py 注册入口 modified 6.44
vllm/v1/kv_offload/cpu/manager.py 卸载管理 modified 6.69
tests/v1/kv_offload/cpu/test_manager.py 测试 modified 6.2

关键符号

CPUOffloadingMetrics CPUOffloadingSpec.build_metric_definitions CPUOffloadingManager.get_stats test_cpu_manager_reports_cache_usage_gauge check_usage_stats

关键源码片段

vllm/v1/kv_offload/cpu/common.py core-logic

定义 `CPUOffloadingMetrics` 类,集中管理所有 CPU 卸载指标的常量名称,是整个指标的源头。

# vllm/v1/kv_offload/cpu/common.py
# CPUOffloadingMetrics 将 CPU 卸载相关的所有 Prometheus 指标名称集中在一个命名空间类下,
# 避免模块级常量分散,同时方便后续增加新指标。
class CPUOffloadingMetrics:
    # 复用已有的 stores_skipped 计数器名称 ( 不变 )
    STORES_SKIPPED = "vllm:kv_offload_stores_skipped"
    # 新引入的 CPU 缓存使用百分比 gauge,命名与 GPU 侧 `vllm:kv_cache_usage_perc` 对齐
    CPU_CACHE_USAGE_PERC = "vllm:kv_offload_cpu_cache_usage_perc"
​
​
class CPULoadStoreSpec(BlockIDsLoadStoreSpec):
    """... unchanged ..."""
tests/v1/kv_offload/cpu/test_manager.py test-coverage

新增的 `test_cpu_manager_reports_cache_usage_gauge` 和辅助函数 `check_usage_stats` 覆盖了各种缓存状态下的使用率计算,确保指标正确性。

# tests/v1/kv_offload/cpu/test_manager.py
# 测试 CPU 缓存使用率 gauge 的准确性def test_cpu_manager_reports_cache_usage_gauge():
    # 辅助检查函数:获取 stats 并断言 gauge 值
    def check_usage_stats(manager: CPUOffloadingManager, value: float):
        stats = manager.get_stats()
        assert stats is not None
        # 使用 approx 处理浮点
        assert stats.reduce()[CPUOffloadingMetrics.CPU_CACHE_USAGE_PERC] == pytest.approx(value)
​
    # 场景 1:零容量管理器始终返回 0.0
    manager = make_cpu_manager(num_blocks=0)
    check_usage_stats(manager, 0.0)
​
    # 场景 2:空管理器(4 块,未分配)返回 0.0
    manager = make_cpu_manager(num_blocks=4)
    check_usage_stats(manager, 0.0)
​
    # 场景 3:分配 2/4 块后返回 0.5
    manager.prepare_store(to_keys([1, 2]), _EMPTY_REQ_CTX)
    check_usage_stats(manager, 0.5)
​
    # 场景 4:填满 4 块后返回 1.0
    manager.prepare_store(to_keys([3, 4]), _EMPTY_REQ_CTX)
    check_usage_stats(manager, 1.0)
​
    # 场景 5:complete_store 后块变为可驱逐,使用率下降
    manager.complete_store(to_keys([1, 2]), _EMPTY_REQ_CTX)
    check_usage_stats(manager, 0.5)
​
    manager.complete_store(to_keys([3, 4]), _EMPTY_REQ_CTX)
    check_usage_stats(manager, 0.0)

评论区精华

build_metric_definitions 的归属:Spec 还是 Manager? 设计

orozery 质疑在 base.py 中给 Manager 添加默认 build_metric_definitions 的必要性,因为已有 Spec 的同名方法。varun 解释初衷是让 Manager 能注册自己特有的指标,但最终接受将指标定义移回 Spec。

结论:指标定义仍然通过 Spec 的 build_metric_definitions 注册,Manager 只负责计算和输出;常量则集中到 CPUOffloadingMetrics 类中。 · 已解决

使用率计算语义:是否包含非活动块? 正确性

varun 指出最初计算(num_allocated_blocks - free_list)会把没有活跃引用的块也算作“已用”,导致指标长期为 1.0。orozery 确认应只计算当前被钉住(pinned)的块(即活跃传输中的块),才与 GPU 侧语义对齐。于是 varun 在原 PR 基础上修改计算,减去 `_num_evictable_cache_blocks`。

结论:使用率 = (已分配块 - 空闲块 - 可驱逐缓存块)/ 总块数, 只反映当前被活跃传输占用的比例。 · 已解决

指标名称与文档字符串的迭代 设计

orozery 建议将指标命名为 `vllm:kv_offload_cpu_cache_usage_perc` 并给出了更详细的文档字符串描述。Srinivasoo7 建议保留 "Experimental" 前缀以符合指标废弃政策。varun 询问是否保留 Experimental 前缀,orozery 和 Srinivasoo7 倾向于保留,但最终合并的版本没有 "Experimental" 前缀,可能是后续决议删除。

结论:最终指标名称为 `vllm:kv_offload_cpu_cache_usage_perc`,文档字符串清晰描述了含义和饱和条件。 · 已解决

测试位置与方式 测试

orozery 反对将 CPU 指标测试放入通用 metrics 测试文件 `test_metrics.py`,认为应放在 CPU 侧测试文件中。varun 遵循了该建议。

结论:测试只保留在 `tests/v1/kv_offload/cpu/test_manager.py` 中。 · 已解决

风险与影响

  1. 行为变化get_stats 现在始终返回 OffloadingConnectorStats 对象,而以前在 store_threshold < 2 时返回 None。调用方需确保不会因为空值检查而错过统计采集。审阅中未发现显式依赖 None 的消费者,但需关注是否有自定义监控脚本依赖旧行为。
  2. 计算准确性:使用率计算依赖于 _num_evictable_cache_blocks 的维护正确性。如果该字段在 eviction 或 cache reset 时未及时更新,会导致指标偏差。
  3. 指标注册:新指标通过 spec.pybuild_metric_definitions 注册,若其他 Spec 也注册同名指标可能导致冲突。目前仅 CPUOffloadingSpec 注册该 gauge,风险可控。

影响范围限于 CPU KV 卸载子系统(vllm/v1/kv_offload/cpu/)。用户可通过 Prometheus 查询 vllm:kv_offload_cpu_cache_usage_perc 指标,用于监控 CPU 缓存饱和度和运维告警。团队需要确保指标名称与 GPU 侧一致,并对后续可能添加的其他卸载后端(如 NPU)给出命名模板。

get_stats 返回值行为变化 新指标计算依赖 _num_evictable_cache_blocks 正确性 指标命名需与 GPU 侧协调

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论