Skip to content

Commit 680d3a5

Browse files
committed
[Refactor] Simplify HMA metrics completion tracking
1 parent af4046f commit 680d3a5

2 files changed

Lines changed: 77 additions & 103 deletions

File tree

test/test_ucm_connector_metrics.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,8 +1049,6 @@ def _new_hma_connector(metadata, fa_store=None, wa_store=None):
10491049
connector.window_group_ids = set()
10501050
connector.group_metas = []
10511051
connector.tp_dump_tasks = {}
1052-
connector._fawa_dump_group_metrics = {}
1053-
connector._fawa_next_dump_group_id = 0
10541052
connector._direct_task_has_load = False
10551053
connector._direct_task_load_duration_ms = 0.0
10561054
connector._direct_task_compute_start_ms = None
@@ -1111,7 +1109,7 @@ def test_hma_connector_load_metrics_ignore_wait_failure_for_counting(monkeypatch
11111109

11121110

11131111
@pytest.mark.parametrize("drain_method", ["get_finished", "preemption"])
1114-
def test_hma_connector_records_grouped_save_completion_metrics(
1112+
def test_hma_connector_records_mixed_fa_wa_save_completion_metrics(
11151113
monkeypatch, drain_method
11161114
):
11171115
_reset_fakes()
@@ -1147,23 +1145,32 @@ def test_hma_connector_records_grouped_save_completion_metrics(
11471145
UCMFAWAConnectorMetadata(preempted_req_ids={"req-1"})
11481146
)
11491147

1150-
completion_stats = fake_ucmmetrics.updated[-2]
1151-
assert completion_stats["save_duration"] == pytest.approx(140.0)
1152-
assert completion_stats["save_speed"] == pytest.approx(240 / 140 / 1024 / 1024)
1153-
assert completion_stats["save_completion_wait_duration"] == pytest.approx(50.0)
1154-
assert completion_stats["save_completion_wait_speed"] == pytest.approx(
1155-
240 / 50 / 1024 / 1024
1156-
)
1157-
task_stats = fake_ucmmetrics.updated[-1]
1158-
assert task_stats["connector_task_load_stage_load_dump_ms"] == 10.0
1159-
assert task_stats["connector_task_compute_stage_load_dump_ms"] == pytest.approx(
1160-
100.0
1161-
)
1162-
assert task_stats["connector_task_dump_stage_load_dump_ms"] == pytest.approx(140.0)
1163-
assert task_stats["connector_task_total_load_dump_ms"] == pytest.approx(250.0)
1148+
assert fake_ucmmetrics.updated[1] == {
1149+
"save_duration": pytest.approx(110.0),
1150+
"save_speed": pytest.approx(200 / 110 / 1024 / 1024),
1151+
"save_completion_wait_duration": pytest.approx(20.0),
1152+
"save_completion_wait_speed": pytest.approx(200 / 20 / 1024 / 1024),
1153+
}
1154+
assert fake_ucmmetrics.updated[2] == {
1155+
"connector_task_load_stage_load_dump_ms": 10.0,
1156+
"connector_task_compute_stage_load_dump_ms": pytest.approx(100.0),
1157+
"connector_task_dump_stage_load_dump_ms": pytest.approx(110.0),
1158+
"connector_task_total_load_dump_ms": pytest.approx(220.0),
1159+
}
1160+
assert fake_ucmmetrics.updated[3] == {
1161+
"save_duration": pytest.approx(130.0),
1162+
"save_speed": pytest.approx(40 / 130 / 1024 / 1024),
1163+
"save_completion_wait_duration": pytest.approx(20.0),
1164+
"save_completion_wait_speed": pytest.approx(40 / 20 / 1024 / 1024),
1165+
}
1166+
assert fake_ucmmetrics.updated[4] == {
1167+
"connector_task_load_stage_load_dump_ms": 10.0,
1168+
"connector_task_compute_stage_load_dump_ms": pytest.approx(100.0),
1169+
"connector_task_dump_stage_load_dump_ms": pytest.approx(130.0),
1170+
"connector_task_total_load_dump_ms": pytest.approx(240.0),
1171+
}
11641172
assert connector.device.destroyed == [11, 12]
11651173
assert connector.tp_dump_tasks == {}
1166-
assert connector._fawa_dump_group_metrics == {}
11671174

11681175

11691176
def test_hma_connector_save_metrics_count_only_submitted_requests_without_changing_tp_keys(

ucm/integration/vllm/hma_connector.py

Lines changed: 52 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -268,20 +268,11 @@ class FAWADumpTask:
268268
task: Task
269269
key_count: int
270270
event_handle: int
271-
metrics_group_id: int
272271
byte_count: int = 0
273272
submit_start_ms: float = 0.0
274-
275-
276-
@dataclass
277-
class FAWADumpGroupMetrics:
278-
save_requests: int
279-
save_blocks: int
280-
save_bytes: int
281-
submit_start_ms: float
282-
direct_task_is_load: bool
283-
direct_task_load_duration_ms: float
284-
direct_task_compute_duration_ms: float
273+
direct_task_is_load: bool = False
274+
direct_task_load_duration_ms: float = 0.0
275+
direct_task_compute_duration_ms: float = 0.0
285276

286277

287278
class UCMFAWAConnector(UCMDirectConnector, SupportsHMA):
@@ -318,8 +309,6 @@ def __init__(
318309
self.requests_meta: dict[str, FAWARequestMeta] = {}
319310
self.tp_dump_tasks: dict[tuple, list[FAWADumpTask]] = {}
320311
self._fawa_store_block_sizes: dict[str, int] = {}
321-
self._fawa_dump_group_metrics: dict[int, FAWADumpGroupMetrics] = {}
322-
self._fawa_next_dump_group_id = 0
323312
self.wa_dump_block_wise = self.launch_config.get("wa_dump_block_wise", True)
324313

325314
if role == KVConnectorRole.SCHEDULER:
@@ -956,7 +945,9 @@ def _submit_dump_task(
956945
keys: list[bytes],
957946
ptrs: np.ndarray,
958947
event_handle: int,
959-
metrics_group_id: int,
948+
direct_task_is_load: bool,
949+
direct_task_load_duration_ms: float,
950+
direct_task_compute_duration_ms: float,
960951
) -> FAWADumpTask:
961952
"""Submit one store dump for FA or WA rows."""
962953

@@ -969,9 +960,11 @@ def _submit_dump_task(
969960
task=task,
970961
key_count=len(keys),
971962
event_handle=event_handle,
972-
metrics_group_id=metrics_group_id,
973963
byte_count=len(keys) * self._fawa_store_block_sizes.get(label, 0),
974964
submit_start_ms=submit_start_ms,
965+
direct_task_is_load=direct_task_is_load,
966+
direct_task_load_duration_ms=direct_task_load_duration_ms,
967+
direct_task_compute_duration_ms=direct_task_compute_duration_ms,
975968
)
976969

977970
def _extract_fa_ptr(self, store_keys, hash_start, hash_end, candidate_vllm_ids):
@@ -1234,10 +1227,13 @@ def wait_for_save(self) -> None:
12341227
)
12351228
)
12361229

1237-
metrics_group_id = -1
1238-
if fa_dump_keys or wa_dump_keys:
1239-
metrics_group_id = self._fawa_next_dump_group_id
1240-
self._fawa_next_dump_group_id += 1
1230+
direct_task_is_load = getattr(self, "_direct_task_has_load", False)
1231+
direct_task_load_duration_ms = getattr(
1232+
self, "_direct_task_load_duration_ms", 0.0
1233+
)
1234+
direct_task_compute_duration_ms = self._current_compute_duration_ms(
1235+
compute_end_time
1236+
)
12411237

12421238
if fa_dump_keys:
12431239
event_handle = self._get_dump_event_handle()
@@ -1251,7 +1247,9 @@ def wait_for_save(self) -> None:
12511247
fa_dump_keys,
12521248
fa_ptrs,
12531249
event_handle,
1254-
metrics_group_id,
1250+
direct_task_is_load,
1251+
direct_task_load_duration_ms,
1252+
direct_task_compute_duration_ms,
12551253
)
12561254
self.tp_dump_tasks[dump_request_ids].append(dump_task)
12571255
submitted_dump_tasks.append(dump_task)
@@ -1270,7 +1268,9 @@ def wait_for_save(self) -> None:
12701268
wa_dump_keys,
12711269
window_ptrs,
12721270
event_handle,
1273-
metrics_group_id,
1271+
direct_task_is_load,
1272+
direct_task_load_duration_ms,
1273+
direct_task_compute_duration_ms,
12741274
)
12751275
self.tp_dump_tasks[dump_request_ids].append(dump_task)
12761276
submitted_dump_tasks.append(dump_task)
@@ -1296,19 +1296,6 @@ def wait_for_save(self) -> None:
12961296
"save_bytes_total": save_bytes,
12971297
}
12981298
)
1299-
self._fawa_dump_group_metrics[metrics_group_id] = FAWADumpGroupMetrics(
1300-
save_requests=save_requests,
1301-
save_blocks=save_blocks,
1302-
save_bytes=save_bytes,
1303-
submit_start_ms=min(task.submit_start_ms for task in submitted_dump_tasks),
1304-
direct_task_is_load=getattr(self, "_direct_task_has_load", False),
1305-
direct_task_load_duration_ms=getattr(
1306-
self, "_direct_task_load_duration_ms", 0.0
1307-
),
1308-
direct_task_compute_duration_ms=self._current_compute_duration_ms(
1309-
compute_end_time
1310-
),
1311-
)
13121299
self._clear_direct_connector_task_state()
13131300

13141301
def _drain_best_effort_dump_tasks(self, finished_req_ids: set[str]) -> None:
@@ -1322,77 +1309,57 @@ def _drain_best_effort_dump_tasks(self, finished_req_ids: set[str]) -> None:
13221309
return
13231310

13241311
finished_chunk_req_ids = []
1325-
completed_group_ids: set[int] = set()
1326-
failed_group_ids: set[int] = set()
1327-
group_wait_start_ms: dict[int, float] = {}
1328-
group_wait_end_ms: dict[int, float] = {}
13291312
for request_ids, dump_tasks in self.tp_dump_tasks.items():
13301313
if finished_req_ids.intersection(request_ids):
13311314
finished_chunk_req_ids.append(request_ids)
13321315
for dump_task in dump_tasks:
1333-
group_id = dump_task.metrics_group_id
13341316
wait_start_ms = time.perf_counter() * 1000
13351317
try:
13361318
dump_task.store.wait(dump_task.task)
13371319
except Exception as e:
1338-
failed_group_ids.add(group_id)
13391320
logger.error(
13401321
"Best-effort FAWA dump task failed; external cache may miss. "
13411322
f"label={dump_task.label}, keys={dump_task.key_count}, "
13421323
f"{type(e).__name__}: {e}"
13431324
)
1344-
finally:
1325+
else:
13451326
wait_end_ms = time.perf_counter() * 1000
1346-
completed_group_ids.add(group_id)
1347-
group_wait_start_ms[group_id] = min(
1348-
group_wait_start_ms.get(group_id, wait_start_ms),
1349-
wait_start_ms,
1327+
wait_duration_ms = self._non_negative_ms(
1328+
wait_end_ms - wait_start_ms
1329+
)
1330+
async_duration_ms = (
1331+
wait_end_ms - dump_task.submit_start_ms
1332+
if dump_task.submit_start_ms > 0
1333+
else wait_duration_ms
1334+
)
1335+
async_duration_ms = self._non_negative_ms(async_duration_ms)
1336+
ucmmetrics.update_stats(
1337+
{
1338+
"save_duration": async_duration_ms,
1339+
"save_speed": self._bytes_per_ms_to_gb_s(
1340+
dump_task.byte_count,
1341+
async_duration_ms,
1342+
),
1343+
"save_completion_wait_duration": wait_duration_ms,
1344+
"save_completion_wait_speed": self._bytes_per_ms_to_gb_s(
1345+
dump_task.byte_count,
1346+
wait_duration_ms,
1347+
),
1348+
}
13501349
)
1351-
group_wait_end_ms[group_id] = max(
1352-
group_wait_end_ms.get(group_id, wait_end_ms),
1353-
wait_end_ms,
1350+
self._record_direct_connector_task_metrics(
1351+
is_load=dump_task.direct_task_is_load,
1352+
is_save=True,
1353+
load_duration_ms=dump_task.direct_task_load_duration_ms,
1354+
compute_duration_ms=dump_task.direct_task_compute_duration_ms,
1355+
dump_duration_ms=async_duration_ms,
13541356
)
1357+
finally:
13551358
self.device.destroy_event_handle(dump_task.event_handle)
13561359

13571360
for request_ids in finished_chunk_req_ids:
13581361
self.tp_dump_tasks.pop(request_ids, None)
13591362

1360-
for group_id in completed_group_ids:
1361-
group_metrics = self._fawa_dump_group_metrics.pop(group_id, None)
1362-
if group_metrics is None or group_id in failed_group_ids:
1363-
continue
1364-
wait_duration_ms = (
1365-
group_wait_end_ms[group_id] - group_wait_start_ms[group_id]
1366-
)
1367-
async_duration_ms = (
1368-
group_wait_end_ms[group_id] - group_metrics.submit_start_ms
1369-
if group_metrics.submit_start_ms > 0
1370-
else wait_duration_ms
1371-
)
1372-
async_duration_ms = self._non_negative_ms(async_duration_ms)
1373-
wait_duration_ms = self._non_negative_ms(wait_duration_ms)
1374-
ucmmetrics.update_stats(
1375-
{
1376-
"save_duration": async_duration_ms,
1377-
"save_speed": self._bytes_per_ms_to_gb_s(
1378-
group_metrics.save_bytes,
1379-
async_duration_ms,
1380-
),
1381-
"save_completion_wait_duration": wait_duration_ms,
1382-
"save_completion_wait_speed": self._bytes_per_ms_to_gb_s(
1383-
group_metrics.save_bytes,
1384-
wait_duration_ms,
1385-
),
1386-
}
1387-
)
1388-
self._record_direct_connector_task_metrics(
1389-
is_load=group_metrics.direct_task_is_load,
1390-
is_save=True,
1391-
load_duration_ms=group_metrics.direct_task_load_duration_ms,
1392-
compute_duration_ms=group_metrics.direct_task_compute_duration_ms,
1393-
dump_duration_ms=async_duration_ms,
1394-
)
1395-
13961363
def handle_preemptions(self, kv_connector_metadata: UCMFAWAConnectorMetadata):
13971364
# Worker side method
13981365
self._drain_best_effort_dump_tasks(kv_connector_metadata.preempted_req_ids)

0 commit comments

Comments
 (0)