main
py 240 lines 5.88 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Default bucket boundaries for the Prometheus histograms emitted by vLLM.
4
5 Every core engine histogram created by the Prometheus stat logger draws its
6 bucket boundaries from exactly one of the families defined here, so each
7 default list has a single source of truth. Histograms owned by other
8 subsystems, such as the KV connector and offloading metrics, keep their own
9 boundaries.
10 """
11
12 from collections.abc import Mapping
13 from typing import Literal, get_args
14
15 BucketFamilyKey = Literal[
16 "request_latency",
17 "time_to_first_token",
18 "inter_token_latency",
19 "iteration_tokens",
20 "request_params_n",
21 "request_num_preemptions",
22 "request_tokens",
23 "kv_cache_residency",
24 ]
25 """Canonical keys for the histogram bucket families."""
26
27 BUCKET_FAMILY_KEYS: frozenset[str] = frozenset(get_args(BucketFamilyKey))
28 """All bucket family keys, for membership checks."""
29
30 REQUEST_LATENCY_BUCKETS: tuple[float, ...] = (
31 0.3,
32 0.5,
33 0.8,
34 1.0,
35 1.5,
36 2.0,
37 2.5,
38 5.0,
39 10.0,
40 15.0,
41 20.0,
42 30.0,
43 40.0,
44 50.0,
45 60.0,
46 120.0,
47 240.0,
48 480.0,
49 960.0,
50 1920.0,
51 7680.0,
52 )
53 """Sub-second scheduling delays through multi-hour batch requests; shared by
54 the accumulated request phase-timing histograms (e2e, queue, inference,
55 prefill, decode)."""
56
57 TIME_TO_FIRST_TOKEN_BUCKETS: tuple[float, ...] = (
58 0.001,
59 0.005,
60 0.01,
61 0.02,
62 0.04,
63 0.06,
64 0.08,
65 0.1,
66 0.25,
67 0.5,
68 0.75,
69 1.0,
70 2.5,
71 5.0,
72 7.5,
73 10.0,
74 20.0,
75 40.0,
76 80.0,
77 160.0,
78 640.0,
79 2560.0,
80 )
81 """Millisecond-scale prefill for tiny prompts up to ~40-minute worst cases,
82 with the densest resolution around interactive (sub-second) latencies."""
83
84 INTER_TOKEN_LATENCY_BUCKETS: tuple[float, ...] = (
85 0.01,
86 0.025,
87 0.05,
88 0.075,
89 0.1,
90 0.15,
91 0.2,
92 0.3,
93 0.4,
94 0.5,
95 0.75,
96 1.0,
97 2.5,
98 5.0,
99 7.5,
100 10.0,
101 20.0,
102 40.0,
103 80.0,
104 )
105 """Per-decode-step latencies: 10 ms fast decode up to multi-second stalls."""
106
107 ITERATION_TOKENS_BUCKETS: tuple[float, ...] = (
108 1,
109 8,
110 16,
111 32,
112 64,
113 128,
114 256,
115 512,
116 1024,
117 2048,
118 4096,
119 8192,
120 16384,
121 )
122 """Tokens processed per engine step: powers of two up to a typical
123 max-num-batched-tokens budget."""
124
125 REQUEST_PARAMS_N_BUCKETS: tuple[float, ...] = (1, 2, 5, 10, 20)
126 """Small integer counts for the ``n`` sampling parameter."""
127
128 REQUEST_NUM_PREEMPTIONS_BUCKETS: tuple[float, ...] = (1, 2, 3, 4, 5, 10, 20)
129 """Per-request preemption counts, recorded for every finished request: the
130 first bucket therefore holds never-preempted requests together with
131 singly-preempted ones, 2 through 5 are separated individually, and the tail
132 coarsens to 20. Distinct from `REQUEST_PARAMS_N_BUCKETS` because these are the
133 boundaries the metric shipped with, not a harmonized choice."""
134
135 KV_CACHE_RESIDENCY_BUCKETS: tuple[float, ...] = (
136 0.001,
137 0.002,
138 0.005,
139 0.01,
140 0.02,
141 0.05,
142 0.1,
143 0.2,
144 0.5,
145 1,
146 2,
147 5,
148 10,
149 20,
150 30,
151 60,
152 120,
153 300,
154 600,
155 1200,
156 1800,
157 )
158 """KV cache block residency times: millisecond-scale reuse gaps up to
159 30-minute block lifetimes."""
160
161 _STATIC_FAMILY_DEFAULTS: dict[str, tuple[float, ...]] = {
162 "request_latency": REQUEST_LATENCY_BUCKETS,
163 "time_to_first_token": TIME_TO_FIRST_TOKEN_BUCKETS,
164 "inter_token_latency": INTER_TOKEN_LATENCY_BUCKETS,
165 "iteration_tokens": ITERATION_TOKENS_BUCKETS,
166 "request_params_n": REQUEST_PARAMS_N_BUCKETS,
167 "request_num_preemptions": REQUEST_NUM_PREEMPTIONS_BUCKETS,
168 "kv_cache_residency": KV_CACHE_RESIDENCY_BUCKETS,
169 }
170
171
172 def build_buckets(mantissa_lst: list[int], max_value: int) -> list[float]:
173 """Build buckets from mantissas scaled by increasing powers of 10.
174
175 Args:
176 mantissa_lst: Mantissa values multiplied by each power of 10.
177 max_value: Largest bucket value to include.
178
179 Returns:
180 Bucket values in increasing order, capped at `max_value`.
181
182 """
183 exponent = 0
184 buckets: list[float] = []
185 while True:
186 for m in mantissa_lst:
187 value = m * 10**exponent
188 if value <= max_value:
189 buckets.append(value)
190 else:
191 return buckets
192 exponent += 1
193
194
195 def build_1_2_5_buckets(max_value: int) -> list[float]:
196 """Build a 1-2-5 series of buckets capped at `max_value`.
197
198 Example:
199 >>> build_1_2_5_buckets(100)
200 [1, 2, 5, 10, 20, 50, 100]
201
202 """
203 return build_buckets([1, 2, 5], max_value)
204
205
206 def histogram_buckets(
207 family: BucketFamilyKey,
208 max_model_len: int | None = None,
209 overrides: Mapping[str, list[float]] | None = None,
210 ) -> list[float]:
211 """Return the bucket boundaries for a histogram family.
212
213 Args:
214 family: Canonical bucket family key.
215 max_model_len: Cap for the token-count series of the
216 `request_tokens` family; required for that family and ignored
217 otherwise.
218 overrides: Mapping from family key to replacement bucket bounds.
219 A present key replaces the family's defaults verbatim. Values
220 must already be validated; the single validation point is
221 `ObservabilityConfig.custom_histogram_buckets`.
222
223 Returns:
224 A fresh list of bucket upper bounds; callers may mutate it freely.
225
226 Raises:
227 ValueError: If `family` is `request_tokens`, no override is given,
228 and `max_model_len` is None.
229
230
231 """
232 if overrides is not None and (custom := overrides.get(family)) is not None:
233 return list(custom)
234 if family == "request_tokens":
235 if max_model_len is None:
236 raise ValueError(
237 "max_model_len is required for the 'request_tokens' bucket family"
238 )
239 return build_1_2_5_buckets(max_model_len)
240 return list(_STATIC_FAMILY_DEFAULTS[family])