Skip to content

Commit 4feff08

Browse files
authored
Merge branch 'main' into zihaok_fix_misc_pyxisruntime_issue
2 parents 7b2d1d3 + de4291f commit 4feff08

13 files changed

Lines changed: 218 additions & 24 deletions

File tree

‎docs/metrics/report_design.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ text prompt with the configured reference tokenizer. This records the workload's
100100
distribution as early as possible. `input_sequence_lengths` is empty when no ISL values were
101101
recorded (for example, no tokenizer is available for text-only inputs).
102102

103+
Set `settings.metrics_isl: false` in YAML, or pass `--no-metrics-isl`, to skip ISL collection.
104+
This avoids all ISL tokenizer work and leaves `input_sequence_lengths` empty. OSL, TPOT,
105+
full-run OSL, and output-derived TPS reporting are unchanged.
106+
103107
**QPS/TPS window selection.** The snapshot always carries both a native window
104108
(`tracked_duration_ns`) and the MLPerf LoadGen "completed" window
105109
(`legacy_loadgen_window_duration_ns`, poisson only), so it stays reinterpretable either way. The

‎src/inference_endpoint/async_utils/services/metrics_aggregator/__main__.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ async def main() -> None:
188188
"sharded pool — one worker process per 8-core block."
189189
),
190190
)
191+
parser.add_argument(
192+
"--metrics-isl",
193+
action=argparse.BooleanOptionalAction,
194+
default=True,
195+
help="Collect input sequence length (ISL) metrics (default: enabled).",
196+
)
191197
parser.add_argument(
192198
"--streaming",
193199
action="store_true",
@@ -280,6 +286,7 @@ async def main() -> None:
280286
args.publish_interval if args.tokenizer_workers > 0 else None
281287
),
282288
streaming=args.streaming,
289+
enable_isl=args.metrics_isl,
283290
shutdown_event=shutdown_event,
284291
drain_timeout_s=args.drain_timeout,
285292
)

‎src/inference_endpoint/async_utils/services/metrics_aggregator/aggregator.py‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ def __init__(
135135
streaming: bool = False,
136136
shutdown_event: asyncio.Event | None = None,
137137
drain_timeout_s: float | None = None,
138+
enable_isl: bool = True,
138139
**kwargs,
139140
):
140141
# drain_timeout_s is injected (not derived) because the right
@@ -179,17 +180,21 @@ def __init__(
179180

180181
# Pre-register all metrics on the registry. Tests can introspect via
181182
# registry.has_counter / has_series.
182-
self._register_metrics(streaming, sig_figs, n_histogram_buckets)
183+
self._register_metrics(streaming, enable_isl, sig_figs, n_histogram_buckets)
183184

184185
self._table = MetricsTable(self._registry)
185-
self._register_triggers(streaming)
186+
self._register_triggers(streaming, enable_isl)
186187

187188
# ------------------------------------------------------------------
188189
# Registration helpers
189190
# ------------------------------------------------------------------
190191

191192
def _register_metrics(
192-
self, streaming: bool, sig_figs: int, n_histogram_buckets: int
193+
self,
194+
streaming: bool,
195+
enable_isl: bool,
196+
sig_figs: int,
197+
n_histogram_buckets: int,
193198
) -> None:
194199
"""Register all counters and series on the registry."""
195200
for key in MetricCounterKey:
@@ -204,13 +209,14 @@ def _register_metrics(
204209
sig_figs=sig_figs,
205210
n_histogram_buckets=n_histogram_buckets,
206211
)
207-
self._registry.register_series(
208-
MetricSeriesKey.ISL.value,
209-
hdr_low=TOKEN_HDR_LOW,
210-
hdr_high=TOKEN_HDR_HIGH,
211-
sig_figs=sig_figs,
212-
n_histogram_buckets=n_histogram_buckets,
213-
)
212+
if enable_isl:
213+
self._registry.register_series(
214+
MetricSeriesKey.ISL.value,
215+
hdr_low=TOKEN_HDR_LOW,
216+
hdr_high=TOKEN_HDR_HIGH,
217+
sig_figs=sig_figs,
218+
n_histogram_buckets=n_histogram_buckets,
219+
)
214220
self._registry.register_series(
215221
MetricSeriesKey.OSL.value,
216222
hdr_low=TOKEN_HDR_LOW,
@@ -246,18 +252,19 @@ def _register_metrics(
246252
dtype=float,
247253
)
248254

249-
def _register_triggers(self, streaming: bool) -> None:
255+
def _register_triggers(self, streaming: bool, enable_isl: bool) -> None:
250256
"""Register metric triggers on the table.
251257
252258
Streaming-only triggers (TTFT, chunk_delta, TPOT) are only registered
253-
when ``streaming=True``.
259+
when ``streaming=True``; the ISL trigger only when ``enable_isl=True``.
254260
"""
255261
table = self._table
256262
registry = self._registry
257263
queue = self._token_queue
258264

259-
# Always registered
260-
table.add_trigger(SampleField.ISSUED_NS, IslTrigger(registry, queue))
265+
# Latency and OSL are always registered; ISL is opt-out.
266+
if enable_isl:
267+
table.add_trigger(SampleField.ISSUED_NS, IslTrigger(registry, queue))
261268
table.add_trigger(SampleField.COMPLETE_NS, SampleLatencyTrigger(registry))
262269
table.add_trigger(SampleField.COMPLETE_NS, OslTrigger(registry, queue))
263270

‎src/inference_endpoint/commands/benchmark/pipeline.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def _build_aggregator_args(
102102
tokenizer_name: str | None,
103103
drain_timeout_s: float | None,
104104
tokenizer_workers: int,
105+
enable_isl: bool,
105106
early_stopping: bool,
106107
) -> list[str]:
107108
"""CLI args for the metrics_aggregator subprocess."""
@@ -124,6 +125,7 @@ def _build_aggregator_args(
124125
if drain_timeout_s is not None:
125126
args.extend(["--drain-timeout", str(drain_timeout_s)])
126127
args.extend(["--tokenizer-workers", str(tokenizer_workers)])
128+
args.append("--metrics-isl" if enable_isl else "--no-metrics-isl")
127129
return args
128130

129131

@@ -294,6 +296,7 @@ async def start(self) -> None:
294296
tokenizer_name=self._tokenizer_name,
295297
drain_timeout_s=timeouts.metrics_drain_timeout_s,
296298
tokenizer_workers=self._config.settings.metrics_tokenizer_workers,
299+
enable_isl=self._config.settings.metrics_isl,
297300
early_stopping=self._config.settings.early_stopping.enabled,
298301
)
299302
event_logger_args = _build_event_logger_args(

‎src/inference_endpoint/config/schema.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,16 @@ class Settings(WithUpdatesMixin, BaseModel):
973973
"(default: 4; 0 = defer everything to the end-of-run drain)."
974974
),
975975
)
976+
metrics_isl: Annotated[
977+
bool,
978+
cyclopts.Parameter(
979+
alias="--metrics-isl",
980+
help="Collect input sequence length (ISL) metrics",
981+
),
982+
] = Field(
983+
True,
984+
description="Collect input sequence length (ISL) metrics (default: true).",
985+
)
976986

977987
@model_validator(mode="after")
978988
def _min_issue_duration_requires_qps(self) -> Self:

‎src/inference_endpoint/config/templates/concurrency_template_full.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ settings:
105105
early_stopping: # MLPerf early-stopping percentile estimates (on by default; enabled: false opts out)
106106
enabled: true # Early-stopping percentile estimates (default on)
107107
metrics_tokenizer_workers: 4 # In-process tokenizer threads for live (mid-run) ISL/OSL/TPOT (default: 4; 0 = defer everything to the end-of-run drain).
108+
metrics_isl: true # Collect input sequence length (ISL) metrics (default: true).
108109
endpoint_config:
109110
endpoints: # Endpoint URL(s). Must include scheme, e.g. 'http://host:port'.
110111
- http://localhost:8000

‎src/inference_endpoint/config/templates/offline_template_full.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ settings:
105105
early_stopping: # MLPerf early-stopping percentile estimates (on by default; enabled: false opts out)
106106
enabled: true # Early-stopping percentile estimates (default on)
107107
metrics_tokenizer_workers: 4 # In-process tokenizer threads for live (mid-run) ISL/OSL/TPOT (default: 4; 0 = defer everything to the end-of-run drain).
108+
metrics_isl: true # Collect input sequence length (ISL) metrics (default: true).
108109
endpoint_config:
109110
endpoints: # Endpoint URL(s). Must include scheme, e.g. 'http://host:port'.
110111
- http://localhost:8000

‎src/inference_endpoint/config/templates/online_template_full.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ settings:
106106
early_stopping: # MLPerf early-stopping percentile estimates (on by default; enabled: false opts out)
107107
enabled: true # Early-stopping percentile estimates (default on)
108108
metrics_tokenizer_workers: 4 # In-process tokenizer threads for live (mid-run) ISL/OSL/TPOT (default: 4; 0 = defer everything to the end-of-run drain).
109+
metrics_isl: true # Collect input sequence length (ISL) metrics (default: true).
109110
endpoint_config:
110111
endpoints: # Endpoint URL(s). Must include scheme, e.g. 'http://host:port'.
111112
- http://localhost:8000

‎tests/unit/async_utils/services/metrics_aggregator/conftest.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ def make_aggregator(
185185
tokenizer=None,
186186
live_flush_interval_s: float | None = None,
187187
streaming: bool = True,
188+
enable_isl: bool = True,
188189
shutdown_event: asyncio.Event | None = None,
189190
drain_timeout_s: float | None = None,
190191
) -> tuple[MetricsAggregatorService, MetricsRegistry, MagicMock]:
@@ -218,6 +219,7 @@ def make_aggregator(
218219
tokenizer=tokenizer,
219220
live_flush_interval_s=live_flush_interval_s,
220221
streaming=streaming,
222+
enable_isl=enable_isl,
221223
shutdown_event=shutdown_event,
222224
drain_timeout_s=drain_timeout_s,
223225
)

‎tests/unit/async_utils/services/metrics_aggregator/test_aggregator.py‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,58 @@ async def test_issued_without_data_no_isl(self, tmp_path):
428428
finally:
429429
agg.close()
430430

431+
@pytest.mark.asyncio
432+
async def test_disabled_isl_skips_prompt_tokenization_keeps_osl(self, tmp_path):
433+
"""enable_isl=False: no ISL series, no prompt enqueued; OSL/TPOT intact."""
434+
loop = asyncio.get_event_loop()
435+
messages = (
436+
{"role": "system", "content": "follow instructions"},
437+
{"role": "user", "content": "summarize this"},
438+
)
439+
with ManagedZMQContext.scoped(socket_dir=str(tmp_path)) as ctx:
440+
agg, registry, _ = make_aggregator(
441+
ctx,
442+
loop,
443+
"agg_isl_disabled",
444+
tokenizer=MockBatchTokenizer(),
445+
enable_isl=False,
446+
)
447+
try:
448+
await agg.process(
449+
[
450+
session_event(
451+
SessionEventType.START_PERFORMANCE_TRACKING, ts=0
452+
),
453+
sample_event(
454+
SampleEventType.ISSUED,
455+
"s1",
456+
ts=1000,
457+
data=PromptData(messages=messages),
458+
),
459+
]
460+
)
461+
assert not registry.has_series(MetricSeriesKey.ISL.value)
462+
assert agg.pending_tokens == 0
463+
464+
await agg.process(
465+
[
466+
sample_event(SampleEventType.RECV_FIRST, "s1", ts=2000),
467+
sample_event(
468+
SampleEventType.COMPLETE,
469+
"s1",
470+
ts=5000,
471+
data=streaming_text("hello", " world", " foo"),
472+
),
473+
session_event(SessionEventType.ENDED, ts=5000),
474+
]
475+
)
476+
assert snapshot_series_total(registry, MetricSeriesKey.OSL.value) == 3
477+
assert snapshot_series_total(
478+
registry, MetricSeriesKey.TPOT_NS.value
479+
) == pytest.approx(1500.0)
480+
finally:
481+
agg.close()
482+
431483

432484
# ---------------------------------------------------------------------------
433485
# Edge cases and event ordering

0 commit comments

Comments
 (0)