Add request-level timestamp for when prefill finishes (#14860)
This commit is contained in:
@@ -425,6 +425,9 @@ class SchedulerDisaggregationPrefillMixin:
|
|||||||
zip(batch.reqs, next_token_ids, strict=True)
|
zip(batch.reqs, next_token_ids, strict=True)
|
||||||
):
|
):
|
||||||
if req.is_chunked <= 0:
|
if req.is_chunked <= 0:
|
||||||
|
if req.time_stats.prefill_finished_ts == 0.0:
|
||||||
|
req.time_stats.prefill_finished_ts = time.time()
|
||||||
|
|
||||||
# There is no output_ids for prefill
|
# There is no output_ids for prefill
|
||||||
req.output_ids.append(next_token_id)
|
req.output_ids.append(next_token_id)
|
||||||
self.tree_cache.cache_unfinished_req(req) # update the tree and lock
|
self.tree_cache.cache_unfinished_req(req) # update the tree and lock
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ class DetokenizerManager(MultiHttpWorkerDetokenizerMixin):
|
|||||||
forward_entry_time=recv_obj.forward_entry_time,
|
forward_entry_time=recv_obj.forward_entry_time,
|
||||||
prefill_launch_delay=recv_obj.prefill_launch_delay,
|
prefill_launch_delay=recv_obj.prefill_launch_delay,
|
||||||
prefill_launch_latency=recv_obj.prefill_launch_latency,
|
prefill_launch_latency=recv_obj.prefill_launch_latency,
|
||||||
|
prefill_finished_ts=recv_obj.prefill_finished_ts,
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle_multimodal_decode_req(self, recv_obj: BatchMultimodalDecodeReq):
|
def handle_multimodal_decode_req(self, recv_obj: BatchMultimodalDecodeReq):
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ class RequestTimingMetricsMixin:
|
|||||||
# Calculated as: prefill_end_time_host - prefill_start_time_host
|
# Calculated as: prefill_end_time_host - prefill_start_time_host
|
||||||
prefill_launch_latency: Optional[List[Optional[float]]]
|
prefill_launch_latency: Optional[List[Optional[float]]]
|
||||||
|
|
||||||
|
# Prefill finished time: timestamp when prefill phase completes (wall clock time).
|
||||||
|
# This marks when the prefill computation finishes.
|
||||||
|
prefill_finished_ts: Optional[List[Optional[float]]]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SpeculativeDecodingMetricsMixin:
|
class SpeculativeDecodingMetricsMixin:
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ def _handle_output_by_index(output, i):
|
|||||||
prefill_launch_latency=_extract_field_by_index(
|
prefill_launch_latency=_extract_field_by_index(
|
||||||
output, "prefill_launch_latency", i
|
output, "prefill_launch_latency", i
|
||||||
),
|
),
|
||||||
|
prefill_finished_ts=_extract_field_by_index(
|
||||||
|
output, "prefill_finished_ts", i
|
||||||
|
),
|
||||||
finished_reasons=_extract_field_by_index(output, "finished_reasons", i),
|
finished_reasons=_extract_field_by_index(output, "finished_reasons", i),
|
||||||
decoded_texts=_extract_field_by_index(output, "decoded_texts", i),
|
decoded_texts=_extract_field_by_index(output, "decoded_texts", i),
|
||||||
decode_ids=_extract_field_by_index(output, "decode_ids", i),
|
decode_ids=_extract_field_by_index(output, "decode_ids", i),
|
||||||
@@ -216,6 +219,9 @@ def _handle_output_by_index(output, i):
|
|||||||
prefill_launch_latency=_extract_field_by_index(
|
prefill_launch_latency=_extract_field_by_index(
|
||||||
output, "prefill_launch_latency", i
|
output, "prefill_launch_latency", i
|
||||||
),
|
),
|
||||||
|
prefill_finished_ts=_extract_field_by_index(
|
||||||
|
output, "prefill_finished_ts", i
|
||||||
|
),
|
||||||
finished_reasons=_extract_field_by_index(output, "finished_reasons", i),
|
finished_reasons=_extract_field_by_index(output, "finished_reasons", i),
|
||||||
output_strs=_extract_field_by_index(output, "output_strs", i),
|
output_strs=_extract_field_by_index(output, "output_strs", i),
|
||||||
output_ids=_extract_field_by_index(output, "output_ids", i),
|
output_ids=_extract_field_by_index(output, "output_ids", i),
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ class SchedulerOutputProcessorMixin:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if req.is_chunked <= 0:
|
if req.is_chunked <= 0:
|
||||||
|
if req.time_stats.prefill_finished_ts == 0.0:
|
||||||
|
req.time_stats.prefill_finished_ts = time.time()
|
||||||
|
|
||||||
# req output_ids are set here
|
# req output_ids are set here
|
||||||
req.output_ids.append(next_token_id)
|
req.output_ids.append(next_token_id)
|
||||||
req.check_finished()
|
req.check_finished()
|
||||||
@@ -793,6 +796,7 @@ class SchedulerOutputProcessorMixin:
|
|||||||
forward_entry_times = []
|
forward_entry_times = []
|
||||||
prefill_launch_delays = []
|
prefill_launch_delays = []
|
||||||
prefill_launch_latencies = []
|
prefill_launch_latencies = []
|
||||||
|
prefill_finished_timestamps = []
|
||||||
|
|
||||||
if return_logprob:
|
if return_logprob:
|
||||||
input_token_logprobs_val = []
|
input_token_logprobs_val = []
|
||||||
@@ -901,6 +905,9 @@ class SchedulerOutputProcessorMixin:
|
|||||||
prefill_launch_latencies.append(
|
prefill_launch_latencies.append(
|
||||||
req.time_stats.get_prefill_launch_latency()
|
req.time_stats.get_prefill_launch_latency()
|
||||||
)
|
)
|
||||||
|
prefill_finished_timestamps.append(
|
||||||
|
req.time_stats.get_prefill_finished_ts()
|
||||||
|
)
|
||||||
|
|
||||||
if not self.spec_algorithm.is_none():
|
if not self.spec_algorithm.is_none():
|
||||||
spec_verify_ct.append(req.spec_verify_ct)
|
spec_verify_ct.append(req.spec_verify_ct)
|
||||||
@@ -1001,6 +1008,7 @@ class SchedulerOutputProcessorMixin:
|
|||||||
forward_entry_time=forward_entry_times,
|
forward_entry_time=forward_entry_times,
|
||||||
prefill_launch_delay=prefill_launch_delays,
|
prefill_launch_delay=prefill_launch_delays,
|
||||||
prefill_launch_latency=prefill_launch_latencies,
|
prefill_launch_latency=prefill_launch_latencies,
|
||||||
|
prefill_finished_ts=prefill_finished_timestamps,
|
||||||
finished_reasons=finished_reasons,
|
finished_reasons=finished_reasons,
|
||||||
decoded_texts=decoded_texts,
|
decoded_texts=decoded_texts,
|
||||||
decode_ids=decode_ids_list,
|
decode_ids=decode_ids_list,
|
||||||
@@ -1044,6 +1052,7 @@ class SchedulerOutputProcessorMixin:
|
|||||||
forward_entry_times = []
|
forward_entry_times = []
|
||||||
prefill_launch_delays = []
|
prefill_launch_delays = []
|
||||||
prefill_launch_latencies = []
|
prefill_launch_latencies = []
|
||||||
|
prefill_finished_timestamps = []
|
||||||
retraction_counts = []
|
retraction_counts = []
|
||||||
for req in reqs:
|
for req in reqs:
|
||||||
if req.finished():
|
if req.finished():
|
||||||
@@ -1061,6 +1070,9 @@ class SchedulerOutputProcessorMixin:
|
|||||||
prefill_launch_latencies.append(
|
prefill_launch_latencies.append(
|
||||||
req.time_stats.get_prefill_launch_latency()
|
req.time_stats.get_prefill_launch_latency()
|
||||||
)
|
)
|
||||||
|
prefill_finished_timestamps.append(
|
||||||
|
req.time_stats.get_prefill_finished_ts()
|
||||||
|
)
|
||||||
retraction_counts.append(req.retraction_count)
|
retraction_counts.append(req.retraction_count)
|
||||||
self.send_to_detokenizer.send_output(
|
self.send_to_detokenizer.send_output(
|
||||||
BatchEmbeddingOutput(
|
BatchEmbeddingOutput(
|
||||||
@@ -1070,6 +1082,7 @@ class SchedulerOutputProcessorMixin:
|
|||||||
forward_entry_time=forward_entry_times,
|
forward_entry_time=forward_entry_times,
|
||||||
prefill_launch_delay=prefill_launch_delays,
|
prefill_launch_delay=prefill_launch_delays,
|
||||||
prefill_launch_latency=prefill_launch_latencies,
|
prefill_launch_latency=prefill_launch_latencies,
|
||||||
|
prefill_finished_ts=prefill_finished_timestamps,
|
||||||
finished_reasons=finished_reasons,
|
finished_reasons=finished_reasons,
|
||||||
embeddings=embeddings,
|
embeddings=embeddings,
|
||||||
prompt_tokens=prompt_tokens,
|
prompt_tokens=prompt_tokens,
|
||||||
|
|||||||
@@ -1538,6 +1538,9 @@ class TokenizerManager(TokenizerCommunicatorMixin, TokenizerManagerMultiItemMixi
|
|||||||
self._add_metric_if_present(
|
self._add_metric_if_present(
|
||||||
recv_obj, "prefill_launch_latency", meta_info, i
|
recv_obj, "prefill_launch_latency", meta_info, i
|
||||||
)
|
)
|
||||||
|
self._add_metric_if_present(
|
||||||
|
recv_obj, "prefill_finished_ts", meta_info, i
|
||||||
|
)
|
||||||
|
|
||||||
if getattr(state.obj, "return_logprob", False):
|
if getattr(state.obj, "return_logprob", False):
|
||||||
self.convert_logprob_style(
|
self.convert_logprob_style(
|
||||||
@@ -1836,12 +1839,6 @@ class TokenizerManager(TokenizerCommunicatorMixin, TokenizerManagerMultiItemMixi
|
|||||||
meta_info["request_sent_to_scheduler_ts"] = (
|
meta_info["request_sent_to_scheduler_ts"] = (
|
||||||
state.request_sent_to_scheduler_ts
|
state.request_sent_to_scheduler_ts
|
||||||
)
|
)
|
||||||
# For embeddings, there's no separate prefill phase, so omit `prefill_finished_ts`.
|
|
||||||
if (
|
|
||||||
not isinstance(recv_obj, BatchEmbeddingOutput)
|
|
||||||
and state.first_token_time > 0
|
|
||||||
):
|
|
||||||
meta_info["prefill_finished_ts"] = state.first_token_time
|
|
||||||
if state.response_sent_to_client_ts > 0:
|
if state.response_sent_to_client_ts > 0:
|
||||||
meta_info["response_sent_to_client_ts"] = state.response_sent_to_client_ts
|
meta_info["response_sent_to_client_ts"] = state.response_sent_to_client_ts
|
||||||
if state.finished_time > 0:
|
if state.finished_time > 0:
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ class TimeStats:
|
|||||||
prefill_start_time_host: float = 0.0
|
prefill_start_time_host: float = 0.0
|
||||||
prefill_end_time_host: float = 0.0
|
prefill_end_time_host: float = 0.0
|
||||||
|
|
||||||
|
# Timestamp when prefill phase finishes, obtained from `time.time()`.
|
||||||
|
# Note that this differs from the other `_time` fields tracked by the
|
||||||
|
# `TimeStats` class, which are obtained from `time.perf_counter()`.
|
||||||
|
# We use `time.time()` instead of `time.perf_counter()` here in order to
|
||||||
|
# maintain unit consistency with other timestamp fields tracked by the `ReqState` class.
|
||||||
|
prefill_finished_ts: float = 0.0
|
||||||
|
|
||||||
def get_queueing_time(self) -> float:
|
def get_queueing_time(self) -> float:
|
||||||
return self.forward_entry_time - self.wait_queue_entry_time
|
return self.forward_entry_time - self.wait_queue_entry_time
|
||||||
|
|
||||||
@@ -77,6 +84,11 @@ class TimeStats:
|
|||||||
return self.prefill_end_time_host - self.prefill_start_time_host
|
return self.prefill_end_time_host - self.prefill_start_time_host
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_prefill_finished_ts(self) -> Optional[float]:
|
||||||
|
if self.prefill_finished_ts > 0.0:
|
||||||
|
return self.prefill_finished_ts
|
||||||
|
return None
|
||||||
|
|
||||||
def convert_to_duration(self) -> str:
|
def convert_to_duration(self) -> str:
|
||||||
if self.disagg_mode == DisaggregationMode.NULL:
|
if self.disagg_mode == DisaggregationMode.NULL:
|
||||||
queue_duration = self.forward_entry_time - self.wait_queue_entry_time
|
queue_duration = self.forward_entry_time - self.wait_queue_entry_time
|
||||||
|
|||||||
Reference in New Issue
Block a user