Skip to content

Commit d4d4f6f

Browse files
committed
Clean up internal tensors
1 parent efb4438 commit d4d4f6f

1 file changed

Lines changed: 63 additions & 159 deletions

File tree

megatron/core/inference/text_generation_controllers/text_generation_controller.py

Lines changed: 63 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,29 @@ def __init__(
8080
def _init_dynamic_sampling_tensors(self):
8181
"""Initialize tensors needed for dynamic sampling."""
8282
context = self.inference_wrapped_model.inference_context
83+
self._materialize_only_last = context.materialize_only_last_token_logits
8384
max_requests = context.max_total_requests
8485

8586
device = torch.cuda.current_device()
8687
logits_dtype = self.inference_wrapped_model.inference_wrapper_config.params_dtype
8788
# Use padded vocab size because tokenizer vocab size might pad to nearest power of 2.
8889
vocab_size = self.inference_wrapped_model.inference_wrapper_config.padded_vocab_size
8990

91+
self._sampling_backend = "torch"
92+
93+
# Keep track of request metadata.
94+
self._active_request_count = None
95+
self._request_metadata: Dict[str, Tensor] = {}
96+
9097
# Initialize bookkeeping tensors.
91-
self.sampling_logits_cuda = torch.empty(
98+
self._sampling_logits_cuda = torch.empty(
9299
max_requests, vocab_size, dtype=logits_dtype, device=device
93100
)
94-
self.sampled_tokens_cuda = torch.empty(max_requests, dtype=torch.int64, device=device)
101+
self._sampled_tokens_cuda = torch.empty(max_requests, dtype=torch.int64, device=device)
95102

96103
# Used for inefficient torch sampling.
97-
self.torch_sampling_buckets: List[Tuple] = []
104+
if self._sampling_backend == "torch":
105+
self._torch_sampling_buckets: List[Tuple] = []
98106

99107
def tokenize_prompt(self, prompt: str, add_BOS: bool = False) -> List[int]:
100108
"""Utility to tokenize the input prompts.
@@ -511,6 +519,13 @@ def _dynamic_step_context_init(
511519
# Turn off symmetric all reduces for prefill
512520
unwrapped_model.set_symmetric_ar(None)
513521

522+
# Get request metadata for this step.
523+
self._active_request_count = context.total_request_count - context.paused_request_count
524+
self._request_metadata = {
525+
label: tensor[context.paused_request_count : context.total_request_count]
526+
for label, tensor in context.request_metadata.items()
527+
}
528+
514529
# Get flat tokens, position ids.
515530
if construct_graph_dimensions is not None:
516531
return context.current_input_and_position_ids(
@@ -531,9 +546,6 @@ def _dynamic_step_forward_logits(self, input_ids: Tensor, position_ids: Tensor)
531546
inference_wrapper_config = self.inference_wrapped_model.inference_wrapper_config
532547

533548
context = self.inference_wrapped_model.inference_context
534-
materialize_only_last_token_logits = context.materialize_only_last_token_logits
535-
536-
active_request_count = context.total_request_count - context.paused_request_count
537549

538550
with torch.inference_mode():
539551
logits = self.inference_wrapped_model.run_one_forward_step(
@@ -542,7 +554,7 @@ def _dynamic_step_forward_logits(self, input_ids: Tensor, position_ids: Tensor)
542554

543555
if self.model_is_pipeline_parallel:
544556
logits_seq_len = (
545-
active_request_count if materialize_only_last_token_logits else input_ids.shape[1]
557+
self._active_request_count if self._materialize_only_last else input_ids.shape[1]
546558
)
547559
vocab_size = inference_wrapper_config.padded_vocab_size
548560
logits_shape = [1, logits_seq_len, vocab_size]
@@ -556,31 +568,22 @@ def _dynamic_step_forward_logits(self, input_ids: Tensor, position_ids: Tensor)
556568
tensor=logits,
557569
pp_group=self.pp_group,
558570
)
559-
return logits
560-
561-
def _dynamic_step_sample_bookkeeping(
562-
self, *, backend: str = "torch", request_metadata: Optional[Dict[str, Tensor]] = None
563-
):
564-
"""Perform bookkeeping necessary to sample logits for dynamic batching.
565571

566-
The ability to override the context's data is solely intended for
567-
standalone use or testing, and should never be used in a running system.
572+
# Last token logits.
573+
if self._materialize_only_last:
574+
# When materialize_only_last_token_logits is true, last_token_logits is
575+
# already called in the forward pass of GPT.
576+
last_token_logits = logits.squeeze(0)
577+
else:
578+
last_token_logits = context.last_token_logits(logits)
579+
# Copy last_token_logits to contiguous buffer.
580+
self._sampling_logits_cuda[:self._active_request_count].copy_(last_token_logits, non_blocking=True)
568581

569-
Args:
570-
backend (str): The sampling backend to use.
571-
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
572-
that manage request metadata, such as sampling parameters. By default, this
573-
metadata is retrieved from the context.
574-
"""
575-
assert backend in ["torch"]
576-
context = self.inference_wrapped_model.inference_context
577-
if request_metadata is None:
578-
request_metadata = {
579-
label: tensor[context.paused_request_count : context.total_request_count]
580-
for label, tensor in context.request_metadata.items()
581-
}
582+
return logits
582583

583-
if backend == "torch":
584+
def _dynamic_step_sample_bookkeeping(self):
585+
"""Perform bookkeeping necessary to sample logits for dynamic batching."""
586+
if self._sampling_backend == "torch":
584587
# Bucketize the core sampling parameters.
585588
# Doing so via list comprehension is orders of magnitude faster than via torch.
586589
temp_hash = {}
@@ -600,144 +603,65 @@ def _dynamic_step_sample_bookkeeping(
600603
del temp_hash, bucket_cnt
601604

602605
# Get representatives for each equivalence class.
603-
temp_reps = request_metadata["temperature"][bucket_reps]
604-
top_k_reps = request_metadata["top_k"][bucket_reps]
605-
top_p_reps = request_metadata["top_p"][bucket_reps]
606+
temp_reps = self._request_metadata["temperature"][bucket_reps]
607+
top_k_reps = self._request_metadata["top_k"][bucket_reps]
608+
top_p_reps = self._request_metadata["top_p"][bucket_reps]
606609

607610
# Store the buckets and their equivalence class representatives.
608-
self.torch_sampling_buckets = (
611+
self._torch_sampling_buckets = (
609612
(sampling_buckets[idx], temp_reps[idx], top_k_reps[idx], top_p_reps[idx])
610613
for idx in range(len(sampling_buckets))
611614
)
612615

613-
return request_metadata
614-
615-
def _dynamic_step_sample_logits(self, logits: Tensor, backend: str = "torch") -> Tensor:
616-
"""Sample tokens from logits for dynamic batching.
617-
618-
Args:
619-
logits (Tensor): The logits to sample from.
620-
backend (str): The sampling backend to use.
621-
622-
Returns:
623-
new_sample (Tensor): The sampled tokens.
624-
"""
616+
def _dynamic_step_sample_logits(self):
617+
"""Sample tokens from logits for dynamic batching."""
625618
# TODO(ksanthanam): Evaluate whether it makes more sense to sample on 1 rank
626619
# and then broadcast the sampled tokens rather than broadcasting the raw logits.
627-
assert backend in ["torch"]
628-
629-
context = self.inference_wrapped_model.inference_context
630-
materialize_only_last_token_logits = context.materialize_only_last_token_logits
631-
632-
# Last token logits.
633-
if materialize_only_last_token_logits:
634-
# When materialize_only_last_token_logits is true, last_token_logits is
635-
# already called in the forward pass of GPT.
636-
last_token_logits = logits.squeeze(0)
637-
else:
638-
last_token_logits = context.last_token_logits(logits)
639-
active_request_count = last_token_logits.size(0)
640-
# Copy last_token_logits to contiguous buffer.
641-
self.sampling_logits_cuda[:active_request_count].copy_(last_token_logits, non_blocking=True)
642-
643-
if backend == "torch":
620+
if self._sampling_backend == "torch":
644621
# Concatenate the outputs once to prevent repeated small writes.
645622
token_list = []
646623
indices_list = []
647624

648-
for indices, temp, top_k, top_p in self.torch_sampling_buckets:
625+
for indices, temp, top_k, top_p in self._torch_sampling_buckets:
649626
sampled_tokens = self._torch_sampling_func(
650-
self.sampling_logits_cuda[indices, :], temp.item(), top_k.item(), top_p.item()
627+
self._sampling_logits_cuda[indices, :], temp.item(), top_k.item(), top_p.item()
651628
)
652629
token_list.append(sampled_tokens)
653630
indices_list.append(indices)
654631

655632
# Single write to the output tensor.
656633
sampled_tokens = torch.cat(token_list, dim=0)
657634
sampled_indices = torch.cat(indices_list, dim=0)
658-
self.sampled_tokens_cuda.index_copy_(0, sampled_indices, sampled_tokens)
659-
return self.sampled_tokens_cuda[:active_request_count].clone()
635+
self._sampled_tokens_cuda.index_copy_(0, sampled_indices, sampled_tokens)
660636

661-
def _dynamic_step_log_probs_bookkeeping(
662-
self, *, request_metadata: Optional[Dict[str, Tensor]] = None
663-
) -> bool:
637+
def _dynamic_step_log_probs_bookkeeping(self) -> Tuple[bool, bool]:
664638
"""Perform bookkeeping necessary to compute log probs for dynamic batching.
665639
666-
Args:
667-
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
668-
that manage request metadata, such as sampling parameters. By default, this
669-
metadata is passed in from `_dynamic_step_sample_bookkeeping`.
670-
671640
Returns:
672641
return_log_probs (bool): Whether to return the sampled log_probs.
673642
"""
674-
context = self.inference_wrapped_model.inference_context
675-
if request_metadata is None:
676-
request_metadata = {
677-
label: tensor[context.paused_request_count : context.total_request_count]
678-
for label, tensor in context.request_metadata.items()
679-
}
680-
materialize_only_last_token_logits = context.materialize_only_last_token_logits
681-
682-
return_log_probs = request_metadata["return_log_probs"]
683-
skip_prompt_log_probs = request_metadata["skip_prompt_log_probs"]
684-
top_n_log_probs = request_metadata["top_n_logprobs"] > 0
685-
686-
to_check_prompt = return_log_probs & ~skip_prompt_log_probs
687-
688-
assert not (to_check_prompt.any() and materialize_only_last_token_logits), (
689-
"Prompt log probs cannot be calculated if only last token logits are materialized. "
690-
"Set materialize_only_last_token_logits to False in DynamicInferenceContext "
691-
"or skip_prompt_log_probs to True in SamplingParams."
692-
)
693-
694-
return return_log_probs.any()
695-
696-
def _dynamic_step_top_n_logprobs_bookkeeping(
697-
self, *, request_metadata: Optional[Dict[str, Tensor]] = None
698-
) -> bool:
699-
"""Perform bookkeeping necessary to compute top-n log probs for dynamic batching.
700-
701-
Args:
702-
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
703-
that manage request metadata, such as sampling parameters. By default, this
704-
metadata is passed in from `_dynamic_step_sample_bookkeeping`.
705-
706-
Returns:
707-
return_top_n_logprobs (bool): Whether to return the sampled top-n log_probs.
708-
"""
709-
context = self.inference_wrapped_model.inference_context
710-
if request_metadata is None:
711-
request_metadata = {
712-
label: tensor[context.paused_request_count : context.total_request_count]
713-
for label, tensor in context.request_metadata.items()
714-
}
643+
return_log_probs = self._request_metadata["return_log_probs"]
644+
skip_prompt_log_probs = self._request_metadata["skip_prompt_log_probs"]
645+
top_n_log_probs = self._request_metadata["top_n_logprobs"] > 0
715646

716-
return_log_probs = request_metadata["return_log_probs"]
717-
skip_prompt_log_probs = request_metadata["skip_prompt_log_probs"]
718-
top_n_log_probs = request_metadata["top_n_logprobs"] > 0
647+
to_check_prompt = (return_log_probs | top_n_log_probs) & ~skip_prompt_log_probs
719648

720-
to_check_prompt = top_n_log_probs & ~skip_prompt_log_probs
721-
722-
assert not (to_check_prompt.any() and materialize_only_last_token_logits), (
649+
assert not (to_check_prompt.any() and self._materialize_only_last), (
723650
"Prompt log probs cannot be calculated if only last token logits are materialized. "
724651
"Set materialize_only_last_token_logits to False in DynamicInferenceContext "
725652
"or skip_prompt_log_probs to True in SamplingParams."
726653
)
727654

728-
return top_n_log_probs.any()
655+
return return_log_probs.any(), top_n_log_probs.any()
729656

730657
def _dynamic_step_calculate_log_probs(self, logits: Tensor) -> Optional[Tensor]:
731658
"""Calculate log probs from logits."""
732659
context = self.inference_wrapped_model.inference_context
733-
materialize_only_last_token_logits = context.materialize_only_last_token_logits
734-
735-
active_request_count = context.total_request_count - context.paused_request_count
736660

737661
return context.calculate_log_probs(
738662
logits,
739-
self.sampled_tokens_cuda[:active_request_count],
740-
only_last_token_logits=materialize_only_last_token_logits,
663+
self._sampled_tokens_cuda[:self._active_request_count],
664+
only_last_token_logits=self._materialize_only_last,
741665
)
742666

743667
def _dynamic_step_calculate_top_n_logprobs(
@@ -761,18 +685,15 @@ def _dynamic_step_calculate_top_n_logprobs(
761685
)
762686

763687
context = self.inference_wrapped_model.inference_context
764-
materialize_only_last_token_logits = context.materialize_only_last_token_logits
765-
766-
active_request_count = context.total_request_count - context.paused_request_count
767688

768689
# Handle decode-only mode (only last token)
769-
if materialize_only_last_token_logits or context.is_decode_only():
690+
if self._materialize_only_last or context.is_decode_only():
770691
# In decode mode or when only last token logits are materialized,
771692
# logits already represent only the last tokens
772-
log_probs = log_probs_tensor[:active_request_count]
693+
log_probs = log_probs_tensor[:self._active_request_count]
773694

774695
top_n_results = {}
775-
for req_idx in range(active_request_count):
696+
for req_idx in range(self._active_request_count):
776697
top_n = int(self.top_n_logprobs_cuda[req_idx].item())
777698
if top_n > 0:
778699
# Get top-n logprobs and indices for this request (single token)
@@ -796,7 +717,7 @@ def _dynamic_step_calculate_top_n_logprobs(
796717
log_probs_per_request = log_probs.split(active_query_lengths.tolist(), dim=0)
797718

798719
top_n_results = {}
799-
for req_idx in range(active_request_count):
720+
for req_idx in range(self._active_request_count):
800721
top_n = int(self.top_n_logprobs_cuda[req_idx].item())
801722
if top_n > 0:
802723
request_log_probs = log_probs_per_request[
@@ -823,31 +744,15 @@ def _dynamic_step_calculate_top_n_logprobs(
823744

824745
return top_n_results if top_n_results else None
825746

826-
def _dynamic_step_context_bookkeeping(
827-
self, new_sample: Tensor, request_metadata: Optional[Dict[str, Tensor]] = None
828-
) -> Dict[str, Tensor]:
747+
def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]:
829748
"""Update the dynamic inference context after sampling.
830749
831-
Args:
832-
new_sample (Tensor): The newly sampled tokens.
833-
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
834-
that manage request metadata, such as sampling parameters. By default, this
835-
metadata is retrieved from the context.
836-
837750
Return:
838751
Dict [str, Tensor]: A dictionary containing:
839752
active_request_ids (Tensor): Current active request IDs.
840753
newly_paused_request_ids (Tensor): Newly paused request IDs.
841754
finished_request_ids (Tensor): Finished request IDs.
842755
"""
843-
context = self.inference_wrapped_model.inference_context
844-
active_request_count = context.total_request_count - context.paused_request_count
845-
if request_metadata is None:
846-
request_metadata = {
847-
label: tensor[context.paused_request_count : context.total_request_count]
848-
for label, tensor in context.request_metadata.items()
849-
}
850-
851756
# Active sequence lengths.
852757
active_request_ids = context.request_ids[
853758
context.paused_request_count : context.total_request_count
@@ -859,15 +764,15 @@ def _dynamic_step_context_bookkeeping(
859764
# Request finished if termination_id or length >= max_sequence_length.
860765
# Note: termination_id tensor has per-request termination IDs from mixed sampling
861766
active_request_mask = (
862-
self.sampled_tokens_cuda[:active_request_count] != request_metadata["termination_id"]
767+
self._sampled_tokens_cuda[:self._active_request_count] != self._request_metadata["termination_id"]
863768
).byte() & torch.less(active_sequence_lengths, max_sequence_lengths).byte()
864769
finished_idxs = (
865770
torch.nonzero(active_request_mask == 0, as_tuple=True)[0] + context.paused_request_count
866771
)
867772
finished_request_ids = context.request_ids[finished_idxs]
868773

869774
# New sample gets updated in update_requests, so we pass in a clone
870-
new_sample_copy = new_sample.clone()
775+
new_sample_copy = self._sampled_tokens_cuda[:self._active_request_count].clone()
871776

872777
# Update requests.
873778
newly_paused_request_ids = context.update_requests(active_request_mask, new_sample_copy)
@@ -919,11 +824,10 @@ async def async_generate_output_tokens_dynamic_batch(
919824
# NOTE [TDE]: This will be moved once CPU and GPU methods are separated.
920825
await asyncio.sleep(0)
921826

922-
request_metadata = self._dynamic_step_sample_bookkeeping()
923-
new_sample = self._dynamic_step_sample_logits(logits)
827+
self._dynamic_step_sample_bookkeeping()
828+
self._dynamic_step_sample_logits()
924829

925-
return_log_probs = self._dynamic_step_log_probs_bookkeeping(request_metadata)
926-
return_top_n_logprobs = self._dynamic_step_top_n_logprobs_bookkeeping(request_metadata)
830+
return_log_probs, return_top_n_log_probs = self._dynamic_step_log_probs_bookkeeping()
927831

928832
log_probs = None
929833
top_n_logprobs = None
@@ -937,10 +841,10 @@ async def async_generate_output_tokens_dynamic_batch(
937841
if skip_bookkeeping:
938842
request_bookkeeping = {}
939843
else:
940-
request_bookkeeping = self._dynamic_step_context_bookkeeping(new_sample)
844+
request_bookkeeping = self._dynamic_step_context_bookkeeping()
941845

942846
ret = {
943-
"sample": new_sample,
847+
"sample": self._sampled_tokens_cuda[:self._active_request_count]
944848
"log_probs": log_probs,
945849
"top_n_logprobs": top_n_logprobs,
946850
"cuda_graph_request_count": cuda_graph_request_count,

0 commit comments

Comments
 (0)