feat(metrics): add scheduler and hiradix cache metrics (#10218) (#10225)

Co-authored-by: Zhiqiang Xie <xiezhq@stanford.edu>
This commit is contained in:
ShawnKung
2025-11-10 18:14:47 +08:00
committed by GitHub
co-authored by Zhiqiang Xie
parent 6f08488042
commit afee2843d5
10 changed files with 210 additions and 9 deletions
+125 -1
View File
@@ -12,6 +12,7 @@
# limitations under the License.
# ==============================================================================
"""Utilities for Prometheus Metrics Collection."""
import os
import time
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Union
@@ -24,6 +25,20 @@ from sglang.srt.utils import get_bool_env_var
SGLANG_TEST_REQUEST_TIME_STATS = get_bool_env_var("SGLANG_TEST_REQUEST_TIME_STATS")
def get_histogram_conf_from_env(env_var_name: str) -> Optional[List[float]]:
"""
Get the histogram configuration from the environment variable.
env value should be like "0.1,0.2,0.5,1,2"
"""
if env_var_name not in os.environ:
return None
# if the env var is not set or empty, return None
env_var_value = os.environ[env_var_name]
if not env_var_value:
return None
return [float(x) for x in env_var_value.split(",")]
@dataclass
class TimeStats:
"""
@@ -184,6 +199,7 @@ class SchedulerStats:
# Engine startup
engine_startup_time: float = 0.0
engine_load_weights_time: float = 0.0
new_token_ratio: float = 0.0
# CUDA graph
is_cuda_graph: float = 0.0
@@ -191,7 +207,10 @@ class SchedulerStats:
class SchedulerMetricsCollector:
def __init__(self, labels: Dict[str, str]) -> None:
def __init__(
self,
labels: Dict[str, str],
) -> None:
# We need to import prometheus_client after setting the env variable `PROMETHEUS_MULTIPROC_DIR`
from prometheus_client import Counter, Gauge, Histogram
@@ -562,6 +581,13 @@ class SchedulerMetricsCollector:
multiprocess_mode="mostrecent",
)
self.new_token_ratio = Gauge(
name="sglang:new_token_ratio",
documentation="The new token ratio.",
labelnames=labels.keys(),
multiprocess_mode="mostrecent",
)
def _log_gauge(self, gauge, data: Union[int, float]) -> None:
# Convenience function for logging to gauge.
gauge.labels(**self.labels).set(data)
@@ -639,6 +665,7 @@ class SchedulerMetricsCollector:
self._log_gauge(
self.engine_load_weights_time, stats.engine_load_weights_time
)
self._log_gauge(self.new_token_ratio, stats.new_token_ratio)
# CUDA graph
self._log_gauge(self.is_cuda_graph, stats.is_cuda_graph)
@@ -1068,3 +1095,100 @@ class ExpertDispatchCollector:
labelnames={"layer"},
buckets=ep_size_buckets,
)
class RadixCacheMetricsCollector:
def __init__(
self,
labels: Dict[str, str],
) -> None:
# We need to import prometheus_client after setting the env variable `PROMETHEUS_MULTIPROC_DIR`
from prometheus_client import Counter, Histogram
self.labels = labels
bucket_eviction_duration = get_histogram_conf_from_env(
"SGLANG_BUCKET_EVICTION_DURATION"
)
if bucket_eviction_duration is None:
bucket_eviction_duration = [
0.001,
0.002,
0.003,
0.004,
0.005,
0.006,
0.007,
0.008,
0.009,
0.01,
0.02,
0.03,
0.04,
0.05,
0.1,
0.2,
0.5,
1.0,
]
bucket_load_back_duration = get_histogram_conf_from_env(
"SGLANG_BUCKET_LOAD_BACK_DURATION"
)
if bucket_load_back_duration is None:
bucket_load_back_duration = [
0.001,
0.002,
0.003,
0.004,
0.005,
0.006,
0.007,
0.008,
0.009,
0.01,
0.02,
0.03,
0.04,
0.05,
0.1,
0.2,
0.5,
1.0,
]
self.eviction_duration_seconds = Histogram(
name="sglang:eviction_duration_seconds",
documentation="Time taken to evict memory from GPU to CPU in seconds.",
labelnames=labels.keys(),
buckets=bucket_eviction_duration,
)
self.eviction_num_tokens = Counter(
name="sglang:evicted_tokens_total",
documentation="The number of tokens evicted from GPU to CPU.",
labelnames=labels.keys(),
)
self.load_back_duration_seconds = Histogram(
name="sglang:load_back_duration_seconds",
documentation="Time taken to load memory from CPU to GPU in seconds.",
labelnames=labels.keys(),
buckets=bucket_load_back_duration,
)
self.load_back_num_tokens = Counter(
name="sglang:load_back_tokens_total",
documentation="The number of tokens loaded from CPU to GPU.",
labelnames=labels.keys(),
)
def increment_eviction_num_tokens(self, num_tokens: int) -> None:
self.eviction_num_tokens.labels(**self.labels).inc(num_tokens)
def increment_load_back_num_tokens(self, num_tokens: int) -> None:
self.load_back_num_tokens.labels(**self.labels).inc(num_tokens)
def observe_eviction_duration(self, duration_seconds: float) -> None:
self.eviction_duration_seconds.labels(**self.labels).observe(duration_seconds)
def observe_load_back_duration(self, duration_seconds: float) -> None:
self.load_back_duration_seconds.labels(**self.labels).observe(duration_seconds)