Skip to content

Commit cb666e9

Browse files
committed
Avoid casting/copying request metadata
1 parent 4fe0985 commit cb666e9

3 files changed

Lines changed: 59 additions & 66 deletions

File tree

megatron/core/inference/contexts/dynamic_context.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,8 @@ def allocate_all_tensors(self, *, is_init: bool) -> None:
538538

539539
# Track request metadata.
540540
self.request_metadata = {
541-
label: torch.empty(
542-
(self.max_total_requests,),
543-
dtype=dtype,
544-
device=torch.cuda.current_device() if on_gpu else torch.device("cpu"),
545-
)
546-
for label, dtype, on_gpu in self.request_metadata_types
541+
label: torch.empty((self.max_total_requests,), dtype=dtype, device=torch.cuda.current_device())
542+
for label, dtype, _ in self.request_metadata_types
547543
}
548544

549545
# Per-token state.
@@ -1054,11 +1050,11 @@ def add_dummy_requests_parallel(
10541050
self.request_output_lengths[request_slice] = lengths_tensor + tokens_to_generate_tensor
10551051
self.request_kv_length_offsets[request_slice] = 0
10561052
self.request_kv_block_counts[request_slice] = block_counts
1057-
for i, (label, dtype, on_gpu) in enumerate(self.request_metadata_types):
1053+
for i, (label, dtype, _) in enumerate(self.request_metadata_types):
10581054
self.request_metadata[label][request_slice] = torch.tensor(
10591055
metadata_cols[i],
10601056
dtype=dtype,
1061-
device=torch.cuda.current_device() if on_gpu else torch.device("cpu"),
1057+
device=torch.cuda.current_device(),
10621058
)
10631059

10641060
dummy_block_idx = self.block_allocator.dummy_block_idx

megatron/core/inference/inference_request.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,13 @@ def get_metadata_types() -> List[Tuple[str, torch.dtype, bool]]:
326326
on_device (bool) - Whether the metadata lives on GPU (True) or CPU (False).
327327
"""
328328
return [
329-
("temperature", torch.float32, True),
330-
("top_k", torch.float32, True), # CPU for torch sampling
331-
("top_p", torch.float32, True), # CPU for torch sampling
332-
("termination_id", torch.float32, True),
333-
("return_log_probs", torch.float32, True), # CPU for non-selective logprobs
334-
("skip_prompt_log_probs", torch.float32, True), # CPU for non-selective logprobs
335-
("top_n_logprobs", torch.float32, True), # CPU for torch sampling
329+
("temperature", torch.float32, False), # CPU for torch sampling
330+
("top_k", torch.int32, False), # CPU for torch sampling
331+
("top_p", torch.float32, False), # CPU for torch sampling
332+
("termination_id", torch.int64, True),
333+
("return_log_probs", torch.bool, False), # CPU for non-selective logprobs
334+
("skip_prompt_log_probs", torch.bool, False), # CPU for non-selective logprobs
335+
("top_n_logprobs", torch.int32, False), # CPU for torch sampling
336336
]
337337

338338
def add_event(self, type: DynamicInferenceEventType, payload: Optional[Any] = None) -> None:

megatron/core/inference/text_generation_controllers/text_generation_controller.py

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,28 @@ def _init_dynamic_sampling_tensors(self):
9090

9191
self._sampling_backend = "torch"
9292

93-
# Keep track of request metadata.
94-
self._active_request_count: int = None
95-
self._active_request_slice: slice = None
96-
self._request_metadata: Tensor = None
97-
9893
# Initialize bookkeeping tensors.
9994
self._sampling_logits_cuda = torch.empty(
10095
max_requests, vocab_size, dtype=logits_dtype, device=device
10196
)
10297
self._sampled_tokens_cuda = torch.empty(max_requests, dtype=torch.int64, device=device)
10398

104-
self.temperature_cuda = torch.empty_like(self._sampled_tokens_cuda, dtype=torch.float)
105-
self.top_k_cuda = torch.empty_like(self._sampled_tokens_cuda, dtype=torch.int32)
106-
self.top_p_cuda = torch.empty_like(self._sampled_tokens_cuda, dtype=torch.float)
107-
self.termination_id_cuda = torch.empty(max_requests, dtype=torch.int64, device=device)
108-
self.return_log_probs_cuda = torch.empty(max_requests, dtype=torch.bool, device=device)
109-
self.skip_prompt_log_probs_cuda = torch.empty(max_requests, dtype=torch.bool, device=device)
110-
self.top_n_logprobs_cuda = torch.empty(max_requests, dtype=torch.int32, device=device)
99+
# Keep track of request metadata.
100+
self._request_metadata_raw = {}
101+
for label, dtype, on_gpu in context.request_metadata_types:
102+
tensor = context.request_metadata[label]
103+
if on_gpu:
104+
# This is just the same Tensor object as in the context.
105+
self._request_metadata_raw[label] = tensor
106+
else:
107+
# Create pinned tensors for request metadata that lives on CPU.
108+
# This is metadata which requires D2H copies, such as top_k for torch sampling.
109+
cpu_tensor = torch.empty_like(tensor, device="cpu", pin_memory=True)
110+
self._request_metadata_raw[label] = cpu_tensor
111+
# These are useful shorthands that are filled in during `_dynamic_step_context_init`.
112+
self._active_request_count: int = None
113+
self._active_request_slice: slice = None
114+
self._request_metadata: Dict[str, Tensor] = {}
111115

112116
# Used for inefficient torch sampling.
113117
if self._sampling_backend == "torch":
@@ -533,10 +537,21 @@ def _dynamic_step_context_init(
533537
self._active_request_slice = slice(
534538
context.paused_request_count, context.total_request_count
535539
)
536-
self._request_metadata = {
537-
label: tensor[self._active_request_slice]
538-
for label, tensor in context.request_metadata.items()
539-
}
540+
for label, dtype, on_gpu in context.request_metadata_types:
541+
self._request_metadata[label] = self._request_metadata_raw[label][
542+
self._active_request_slice
543+
]
544+
if on_gpu:
545+
# _request_metadata_raw is just a pointer to the Tensor objects in the context.
546+
pass
547+
if not on_gpu:
548+
# We need a D2H copy from the context to the pinned memory buffer.
549+
self._request_metadata[label].copy_(
550+
context.request_metadata[label][
551+
self._active_request_slice
552+
],
553+
non_blocking=True,
554+
)
540555

541556
# Get flat tokens, position ids.
542557
if construct_graph_dimensions is not None:
@@ -596,27 +611,6 @@ def _dynamic_step_forward_logits(self, input_ids: Tensor, position_ids: Tensor)
596611
def _dynamic_step_sample_bookkeeping(self):
597612
"""Perform bookkeeping necessary to sample logits for dynamic batching."""
598613

599-
# Copy data into relevant tensors.
600-
self.temperature_cuda[:self._active_request_count].copy_(
601-
self._request_metadata["temperature"], non_blocking=True
602-
)
603-
self.top_k_cuda[:self._active_request_count] = self._request_metadata["top_k"].to(
604-
dtype=torch.int32, copy=True, non_blocking=True
605-
)
606-
self.top_p_cuda[:self._active_request_count].copy_(self._request_metadata["top_p"], non_blocking=True)
607-
self.termination_id_cuda[:self._active_request_count] = self._request_metadata["termination_id"].to(
608-
dtype=torch.int64, copy=True, non_blocking=True
609-
)
610-
self.return_log_probs_cuda[:self._active_request_count] = self._request_metadata["return_log_probs"].to(
611-
dtype=torch.bool, copy=True, non_blocking=True
612-
)
613-
self.skip_prompt_log_probs_cuda[:self._active_request_count] = self._request_metadata[
614-
"skip_prompt_log_probs"
615-
].to(dtype=torch.bool, copy=True, non_blocking=True)
616-
self.top_n_logprobs_cuda[:self._active_request_count] = self._request_metadata[
617-
"top_n_logprobs"
618-
].to(dtype=torch.int32, copy=True, non_blocking=True)
619-
620614
if self._sampling_backend == "torch":
621615
# Bucketize the core sampling parameters.
622616
core_params = torch.stack(
@@ -632,12 +626,10 @@ def _dynamic_step_sample_bookkeeping(self):
632626
)
633627
order = torch.argsort(inv_indices, stable=True)
634628
sampling_buckets = torch.split(order, cnts.tolist())
635-
# Perform the D2H sync needed by `_torch_sampling_func` here.
636629
group_reps = torch.stack([indices[0] for indices in sampling_buckets], dim=0)
637-
core_params_reps = core_params[group_reps].detach().cpu()
638-
temp_reps = core_params_reps[:, 0].tolist()
639-
top_k_reps = core_params_reps[:, 1].to(torch.int32).tolist()
640-
top_p_reps = core_params_reps[:, 2].tolist()
630+
temp_reps = self._request_metadata["temperature"][group_reps].tolist()
631+
top_k_reps = self._request_metadata["top_k"][group_reps].tolist()
632+
top_p_reps = self._request_metadata["top_p"][group_reps].tolist()
641633

642634
# Store the buckets and their equivalence class representatives.
643635
self._torch_sampling_buckets = (
@@ -665,17 +657,17 @@ def _dynamic_step_sample_logits(self):
665657
# Single write to the output tensor.
666658
sampled_tokens = torch.cat(token_list, dim=0)
667659
sampled_indices = torch.cat(indices_list, dim=0)
668-
self._sampled_tokens_cuda.index_copy_(0, sampled_indices, sampled_tokens)
660+
self._sampled_tokens_cuda[sampled_indices] = sampled_tokens
669661

670662
def _dynamic_step_log_probs_bookkeeping(self) -> Tuple[bool, bool]:
671663
"""Perform bookkeeping necessary to compute log probs for dynamic batching.
672664
673665
Returns:
674666
return_log_probs (bool): Whether to return the sampled log_probs.
675667
"""
676-
return_log_probs = self.return_log_probs_cuda[:self._active_request_count]
677-
skip_prompt_log_probs = self.skip_prompt_log_probs_cuda[:self._active_request_count]
678-
top_n_log_probs = self.top_n_logprobs_cuda[:self._active_request_count] > 0
668+
return_log_probs = self._request_metadata["return_log_probs"]
669+
skip_prompt_log_probs = self._request_metadata["skip_prompt_log_probs"]
670+
top_n_log_probs = self._request_metadata["top_n_logprobs"] > 0
679671

680672
to_check_prompt = (return_log_probs | top_n_log_probs) & ~skip_prompt_log_probs
681673

@@ -727,7 +719,7 @@ def _dynamic_step_calculate_top_n_logprobs(
727719

728720
top_n_results = {}
729721
for req_idx in range(self._active_request_count):
730-
top_n = int(self.top_n_logprobs_cuda[req_idx].item())
722+
top_n = int(self._request_metadata["top_n_logprobs"][req_idx].item())
731723
if top_n > 0:
732724
# Get top-n logprobs and indices for this request (single token)
733725
top_n_logits = torch.topk(log_probs[req_idx], k=top_n)
@@ -751,12 +743,12 @@ def _dynamic_step_calculate_top_n_logprobs(
751743

752744
top_n_results = {}
753745
for req_idx in range(self._active_request_count):
754-
top_n = int(self.top_n_logprobs_cuda[req_idx].item())
746+
top_n = int(self._request_metadata["top_n_logprobs"][req_idx].item())
755747
if top_n > 0:
756748
request_log_probs = log_probs_per_request[
757749
req_idx
758750
] # [num_tokens_for_request, vocab_size]
759-
skip_prompt = bool(self.skip_prompt_log_probs_cuda[req_idx].item())
751+
skip_prompt = bool(self._request_metadata["skip_prompt_log_probs"][req_idx].item())
760752

761753
# If skip_prompt_log_probs is True, only compute for last token
762754
if skip_prompt and request_log_probs.size(0) > 1:
@@ -780,6 +772,12 @@ def _dynamic_step_calculate_top_n_logprobs(
780772
def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]:
781773
"""Update the dynamic inference context after sampling.
782774
775+
Args:
776+
new_sample (Tensor): The newly sampled tokens.
777+
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
778+
that manage request metadata, such as sampling parameters. By default, this
779+
metadata is retrieved from the context.
780+
783781
Return:
784782
Dict [str, Tensor]: A dictionary containing:
785783
active_request_ids (Tensor): Current active request IDs.
@@ -799,8 +797,7 @@ def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]:
799797
# Request finished if termination_id or length >= max_sequence_length.
800798
# Note: termination_id tensor has per-request termination IDs from mixed sampling
801799
active_request_mask = (
802-
self._sampled_tokens_cuda[:self._active_request_count]
803-
!= self.termination_id_cuda[:self._active_request_count]
800+
self._sampled_tokens_cuda[:self._active_request_count] != self._request_metadata["termination_id"]
804801
).byte() & torch.less(active_sequence_lengths, max_sequence_lengths).byte()
805802
finished_idxs = (
806803
torch.nonzero(active_request_mask == 0, as_tuple=True)[0] + context.paused_request_count

0 commit comments

Comments
 (0)