# PR #24458 完整报告

- 仓库：`sgl-project/sglang`
- 标题：Add fwd_occupancy metric to SchedulerStats and Prometheus collector
- 合并时间：2026-05-06 08:04
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/24458

---

# 执行摘要

- 一句话：新增 fwd_occupancy 指标并暴露到 Prometheus
- 推荐动作：值得快速合并。该 PR 改动量小、逻辑清晰，且补充了关键的可观测性指标。建议在合并后补充自动化测试，确保 `/metrics` 端点包含该指标。同时，后续可以考虑集成 GPU 硬件占用率作为补充。

# 功能与动机

为了增强 GPU 前向传递占用率的可观测性，让运维和性能分析人员能够通过 /metrics 端点获取 sglang:fwd_occupancy 数值，从而更好地了解 GPU 利用情况。

# 实现拆解

1. **SchedulerStats 数据类增加字段 **（python/sglang/srt/observability/metrics_collector.py）：在 `SchedulerStats` 数据类中，于 `utilization` 字段之后新增 `fwd_occupancy: float = float("nan")`。该字段默认为 NaN，表示未启用或尚未有数据。
2. **Prometheus Gauge 注册 **（python/sglang/srt/observability/metrics_collector.py）：在 `SchedulerMetricsCollector.__init__` 中，在 `utilization` Gauge 之后添加一个名为 `sglang:fwd_occupancy` 的 Gauge，标签与其它指标保持一致，文档说明为“Forward pass GPU occupancy percentage.”。
3. **汇报逻辑集成 **（python/sglang/srt/observability/scheduler_metrics_mixin.py）：在 `report_prefill_stats` 和 `report_decode_stats` 两个方法中，在 `self.calculate_utilization()` 之后将 `self.fwd_occupancy` 赋值给 `self.stats.fwd_occupancy`，确保指标在每次统计周期中被更新。
4. **初始化位置调整**：提交历史显示，第二个 commit 将 `self.fwd_occupancy = float("nan")` 从 `ENABLE_METRICS_DEVICE_TIMER` 条件块中移出，改为无条件初始化。这样即使 `ENABLE_METRICS_DEVICE_TIMER` 为 False，也能保证 `fwd_occupancy` 属性存在，避免访问时的 AttributeError。
5. **指标收集**：在 `log_stats` 方法中，通过 `self._log_gauge(self.fwd_occupancy, stats.fwd_occupancy)` 将数值推送给 Prometheus。

关键文件：
- `python/sglang/srt/observability/metrics_collector.py`（模块 可观测性；类别 source；类型 core-logic；符号 SchedulerStats, SchedulerMetricsCollector）: 在此文件中，SchedulerStats 数据类新增了 fwd_occupancy 字段，SchedulerMetricsCollector 注册了对应的 Prometheus Gauge，并在 log_stats 方法中添加了指标收集逻辑。
- `python/sglang/srt/observability/scheduler_metrics_mixin.py`（模块 可观测性；类别 source；类型 core-logic；符号 init_metrics, report_prefill_stats, report_decode_stats）: 在此文件中，fwd_occupancy 属性的初始化从条件块中移出变为无条件，并在 report_prefill_stats 和 report_decode_stats 方法中将该字段赋值给 stats 对象，确保指标在每次统计周期中被更新。

关键符号：init_metrics, report_prefill_stats, report_decode_stats, log_stats

## 关键源码片段

### `python/sglang/srt/observability/metrics_collector.py`

在此文件中，SchedulerStats 数据类新增了 fwd_occupancy 字段，SchedulerMetricsCollector 注册了对应的 Prometheus Gauge，并在 log_stats 方法中添加了指标收集逻辑。

```python
# python/sglang/srt/observability/metrics_collector.py

@dataclass
class SchedulerStats:
    ...
    # Utilization
    utilization: float = 0.0
    # 新增字段：Forward pass GPU occupancy percentage
    fwd_occupancy: float = float("nan")
    ...

class SchedulerMetricsCollector:
    def __init__(self, labels, ...):
        ...
        # 注册 fwd_occupancy Gauge
        self.fwd_occupancy = Gauge(
            name="sglang:fwd_occupancy",
            documentation="Forward pass GPU occupancy percentage.",
            labelnames=labels.keys(),
            multiprocess_mode="mostrecent",
        )
        ...

    def log_stats(self, stats: SchedulerStats) -> None:
        ...
        # Utilization
        self._log_gauge(self.utilization, stats.utilization)
        # 新增：收集 fwd_occupancy 指标
        self._log_gauge(self.fwd_occupancy, stats.fwd_occupancy)
        ...

```

### `python/sglang/srt/observability/scheduler_metrics_mixin.py`

在此文件中，fwd_occupancy 属性的初始化从条件块中移出变为无条件，并在 report_prefill_stats 和 report_decode_stats 方法中将该字段赋值给 stats 对象，确保指标在每次统计周期中被更新。

```python
# python/sglang/srt/observability/scheduler_metrics_mixin.py

class SchedulerMetricsMixin:
    def init_metrics(self):
        ...
        # 无条件初始化 fwd_occupancy，避免当 ENABLE_METRICS_DEVICE_TIMER 关闭时属性缺失
        self.fwd_occupancy = float("nan")

        if ENABLE_METRICS_DEVICE_TIMER:
            # 原有的设备定时器相关初始化，不再包含 fwd_occupancy
            ...
        ...

    def report_prefill_stats(self):
        ...
        # 记录 fwd_occupancy
        self.stats.fwd_occupancy = self.fwd_occupancy
        ...

    def report_decode_stats(self):
        ...
        # 记录 fwd_occupancy
        self.stats.fwd_occupancy = self.fwd_occupancy
        ...

```

# 评论区精华

该 PR 没有 review 讨论。但提交历史中有一条重要调整：第二个 commit 将 `fwd_occupancy` 的初始化从 `ENABLE_METRICS_DEVICE_TIMER` 条件块中移出，改为无条件初始化。这一改动避免了当特性开关关闭时直接访问 `self.fwd_occupancy` 导致的属性缺失错误，体现了对运行健壮性的考量。

- 暂无高价值评论线程

# 风险与影响

- 风险：
 - **回归风险**：低。新增字段默认值为 NaN，不影响现有逻辑。
 - **性能风险**：低。仅新增一个 Gauge 汇报，开销可忽略。
 - **兼容性**：无。指标为新增，不涉及已有字段或 API 变更。
 - **缺失测试**：PR body 列出了两个手动验证步骤，但无自动化测试覆盖。生产环境需注意 `/metrics` 端点的实际输出验证。
- 影响：
 - **用户 / 运维**：可以在 Prometheus 中查询 `sglang:fwd_occupancy` 指标，监控 GPU 前向占用率，提升可观测性。
 - **系统**：无影响。
 - **团队**：为后续的利用率分析和调度优化提供了数据基础。
 - 风险标记：缺少测试覆盖

# 关联脉络

- PR #24396 [sgl] expose swa and mamba cache metrics: 同样是在 observability 模块中新增指标（SWA/Mamba 缓存容量），与本 PR 属于同一类可观测性增强工作。