Skip to content

Commit 5fc20a5

Browse files
committed
feat: Refactor TPUPerformanceAnalyzer to enhance streaming rate verification with dynamic task groups
1 parent 8bde730 commit 5fc20a5

1 file changed

Lines changed: 38 additions & 51 deletions

File tree

dags/tpu_observability/tpu_info_streaming_rate.py

Lines changed: 38 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import re
2222
import subprocess
2323
import tempfile
24-
from typing import List
2524
import logging
2625

2726
from airflow import models
@@ -50,7 +49,6 @@ def __init__(self, target_rate: float = 0.1):
5049
self.frames = []
5150
self.update_events = []
5251

53-
# Pre-compile regex patterns for optimized performance
5452
self._frame_start_re = re.compile(r"\[(\d{2}:\d{2}:\d{2}\.\d{3})\].*?\[H")
5553
self._chips_re = re.compile(
5654
r"│\s+(/dev/vfio/\d+)\s+│.*?│\s+\d+\s+│\s+(\d+)\s+│"
@@ -104,7 +102,7 @@ def parse_log(self, log_content: str):
104102
if current_frame["ts"]:
105103
self.frames.append(current_frame)
106104
current_frame = self._init_empty_frame()
107-
current_frame["ts"] = datetime.strptime(
105+
current_frame["ts"] = datetime.datetime.strptime(
108106
fs_match.group(1), "%H:%M:%S.%f"
109107
)
110108
continue
@@ -120,7 +118,6 @@ def filter_update_events(self):
120118
self.update_events = []
121119
last_snapshot = None
122120
for f in self.frames:
123-
# Create a snapshot excluding the timestamp for comparison
124121
snapshot = {k: v for k, v in f.items() if k != "ts"}
125122
if last_snapshot is None or snapshot != last_snapshot:
126123
self.update_events.append(f)
@@ -223,7 +220,7 @@ def execute_tpu_info_cli_command(info, pod_name: str, tpu_args: str) -> str:
223220

224221

225222
def verify_output_contains_patterns(
226-
output: str, patterns: List[str], context: str
223+
output: str, patterns: list[str], context: str
227224
):
228225
"""Verifies that expected strings exist in the output."""
229226
for pattern in patterns:
@@ -233,36 +230,18 @@ def verify_output_contains_patterns(
233230
)
234231

235232

236-
@task
237-
def validate_streaming_rate(info, pod_name: str, rate: float) -> str:
238-
"""
239-
Executes tpu-info --streaming and validates frequency using UI and Data metrics.
240-
"""
241-
duration = 15
242-
243-
tpu_args = (
244-
f"sh -c \"script -q -c 'timeout {duration}s "
245-
f"tpu-info --streaming --rate {rate}' /dev/null\" "
246-
f"|| [ $? -eq 124 ]"
247-
)
248-
output = execute_tpu_info_cli_command(info, pod_name, tpu_args)
249-
250-
patterns = ["Refresh rate:", f"{rate}s"]
251-
verify_output_contains_patterns(
252-
output, patterns, f"Content check on {pod_name}"
253-
)
254-
return f"Validated {pod_name} at {rate}s"
255-
256-
257233
@task
258234
def validate_streaming_rate_iterations(
259-
info, pod_name: str, rate: float, iteration_count: int = 40
235+
info,
236+
pod_name: str,
237+
rate: float,
238+
iteration_count: int = 40,
239+
duration: int = 30,
260240
) -> str:
261241
"""
262242
Performs 40 iterations of 30s tests.
263243
The task succeeds if at least 50% of the iterations are valid.
264244
"""
265-
duration = 30
266245
analyzer = TPUPerformanceAnalyzer(target_rate=rate)
267246
success_count = 0
268247
pass_threshold = iteration_count / 2 # 50% threshold
@@ -286,14 +265,14 @@ def validate_streaming_rate_iterations(
286265
logging.info(analyzer.generate_report())
287266
if analyzer.validate_rate_match():
288267
success_count += 1
268+
logging.info(f"Iteration {i}: Passed validation.")
289269
else:
290270
logging.info(
291271
f"Iteration {i}: Failed validation (Intervals out of range)."
292272
)
293273
except Exception as e:
294274
logging.error(f"Iteration {i}: Command execution error: {str(e)}")
295275

296-
# Evaluation logic: Pass if success_count >= 20
297276
status_msg = f"Pod {pod_name} at {rate}s: {success_count}/{iteration_count} iterations passed."
298277
logging.info(status_msg)
299278

@@ -376,7 +355,9 @@ def generate_second_node_pool_name(
376355
tpu_topology=config.tpu_topology,
377356
)
378357

379-
cluster_info_2 = node_pool.copy_node_pool_info_with_override(
358+
cluster_info_2 = node_pool.copy_node_pool_info_with_override.override(
359+
task_id="copy_node_pool_info_with_override"
360+
)(
380361
info=cluster_info,
381362
node_pool_name=generate_second_node_pool_name(cluster_info),
382363
)
@@ -414,26 +395,32 @@ def generate_second_node_pool_name(
414395
task_id="wait_for_job_start"
415396
)(cluster_info, pod_name_list=pod_names, job_apply_time=apply_time)
416397

417-
test_rates = [0.1, 0.5, 1.0, 5.0]
418-
rate_test_groups = []
419-
for rate in test_rates:
420-
formatted_rate = str(rate).replace(".", "_")
421-
422-
with TaskGroup(
423-
group_id=f"verification_group_rate_{formatted_rate}"
424-
) as rate_group:
425-
# Fix: Ensure parameters match the task signature exactly
426-
validate_streaming_rate_iterations.override(
427-
task_id="streaming_rate_test",
428-
execution_timeout=datetime.timedelta(minutes=60),
429-
).partial(
430-
info=cluster_info,
431-
rate=rate,
432-
iteration_count=40, # Pass integer directly to partial
433-
).expand(
434-
pod_name=pod_names
435-
)
436-
rate_test_groups.append(rate_group)
398+
# Keyword arguments are generated dynamically at runtime (pylint does not
399+
# know this signature).
400+
with TaskGroup( # pylint: disable=unexpected-keyword-arg
401+
group_id="tpu_streaming_rate_verification"
402+
) as rate_verification_group:
403+
test_rates = [0.1, 0.5, 1.0, 5.0]
404+
405+
for rate in test_rates:
406+
formatted_rate = str(rate).replace(".", "_")
407+
408+
# Keyword arguments are generated dynamically at runtime (pylint does not
409+
# know this signature).
410+
with TaskGroup( # pylint: disable=unexpected-keyword-arg
411+
group_id=f"rate_{formatted_rate}"
412+
) as rate_iterations_group:
413+
validate_streaming_rate_iterations.override(
414+
task_id="streaming_rate_test",
415+
execution_timeout=datetime.timedelta(minutes=60),
416+
duration=30, # Each iteration runs for 30 seconds
417+
).partial(
418+
info=cluster_info,
419+
rate=rate,
420+
iteration_count=40,
421+
).expand(
422+
pod_name=pod_names
423+
)
437424

438425
cleanup_workload = jobset.end_workload.override(
439426
task_id="cleanup_workload", trigger_rule=TriggerRule.ALL_DONE
@@ -474,7 +461,7 @@ def generate_second_node_pool_name(
474461
>> apply_time
475462
>> pod_names
476463
>> wait_for_job_start
477-
>> rate_test_groups
464+
>> rate_verification_group
478465
>> cleanup_workload
479466
>> cleanup_node_pool
480467
)

0 commit comments

Comments
 (0)