Skip to content

Commit 54e69b7

Browse files
committed
Split request_metadata 2D tensor into 1D slices
1 parent d61029f commit 54e69b7

4 files changed

Lines changed: 109 additions & 81 deletions

File tree

megatron/core/inference/contexts/dynamic_context.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,9 @@ class DynamicInferenceContext(BaseInferenceContext):
239239
use_flashinfer_fused_rope (bool): If True, use flashinfer's fused rope implementation.
240240
If None, defaults to using flash-infer if available.
241241
metrics_writer (Optional['WandbModule']): Wandb module for writing metrics.
242-
num_request_metadata (Optional[int]): Number of metadata fields to track per request.
243-
These represent metadata that is needed by the text generation controller,
244-
and that must be kept in sync with active requests through update_requests.
242+
request_metadata_types (Optional[List[Tuple[str, torch.dtype, bool]]]): A list of the
243+
per-request metadata types to track. Each entry is a tuple consisting of the string
244+
label, the target dtype, and whether to store the data on GPU.
245245
"""
246246

247247
DEFAULT_MAX_TOKENS = 16384
@@ -270,7 +270,7 @@ def __init__(
270270
use_flashinfer_fused_rope: bool = False,
271271
unified_memory_level: Optional[int] = 1,
272272
metrics_writer: Optional['WandbModule'] = None,
273-
num_request_metadata: Optional[int] = None,
273+
request_metadata_types: Optional[List[Tuple[str, torch.dtype, bool]]] = None,
274274
):
275275
super().__init__(materialize_only_last_token_logits=materialize_only_last_token_logits)
276276

@@ -395,9 +395,9 @@ def __init__(
395395
)
396396

397397
# Track request metadata.
398-
if num_request_metadata is None:
399-
num_request_metadata = len(DynamicInferenceRequest.get_metadata_labels())
400-
self.num_request_metadata = num_request_metadata
398+
if request_metadata_types is None:
399+
request_metadata_types = DynamicInferenceRequest.get_metadata_types()
400+
self.request_metadata_types = request_metadata_types
401401

402402
# Initialize context state.
403403
self.params_dtype = params_dtype
@@ -554,11 +554,14 @@ def allocate_all_tensors(self, *, is_init: bool) -> None:
554554
)
555555

556556
# Track request metadata.
557-
self.request_metadata = torch.empty(
558-
(self.max_total_requests, self.num_request_metadata),
559-
dtype=torch.float32,
560-
device=torch.cuda.current_device(),
561-
)
557+
self.request_metadata = {
558+
label: torch.empty(
559+
(self.max_total_requests,),
560+
dtype=dtype,
561+
device=torch.cuda.current_device() if on_gpu else torch.device("cpu"),
562+
)
563+
for label, dtype, on_gpu in self.request_metadata_types
564+
}
562565

563566
# Per-token state.
564567
self.token_to_input_ids = torch.full(
@@ -1170,7 +1173,10 @@ def reset(self) -> None:
11701173
self.request_last_kv_block_id.fill_(-1)
11711174
self.request_last_kv_block_offset.fill_(0)
11721175
self.request_to_kv_block_ids.fill_(-1)
1173-
self.request_metadata.fill_(0)
1176+
1177+
# Reset request metadata.
1178+
for metadata_tensor in self.request_metadata.values():
1179+
metadata_tensor.fill_(0)
11741180

11751181
# Reset token indexes.
11761182
self.token_to_input_ids.fill_(0)
@@ -1322,14 +1328,17 @@ def add_request(self, req: DynamicInferenceRequest, chunk_length: Optional[int]
13221328
raise TokenOverflowError(req.request_id)
13231329

13241330
self.request_ids[current_id] = req.request_id
1331+
13251332
# Handle request metadata.
1326-
metadata = req.tracked_metadata
13271333
assert (
1328-
len(metadata) == self.num_request_metadata
1329-
), "Request added to context with invalid metadata length"
1330-
self.request_metadata[current_id] = torch.tensor(
1331-
metadata, dtype=torch.float32, device=self.request_metadata.device
1332-
)
1334+
req.get_metadata_types() == self.request_metadata_types
1335+
), "Request added to context with invalid metadata types"
1336+
metadata = req.tracked_metadata
1337+
metadata_types = req.get_metadata_types()
1338+
for m, m_type in zip(metadata, metadata_types):
1339+
label, _, _ = m_type
1340+
self.request_metadata[label][current_id] = m
1341+
13331342
# Handle length and block assignments.
13341343
self.request_query_lengths[current_id] = chunk_length
13351344
self.request_output_lengths[current_id] = (
@@ -1395,7 +1404,6 @@ def _move_book_keeping_tensors(self, src_idxs, dst_idxs, next_tokens):
13951404
self.request_kv_length_offsets[dst_idxs] = self.request_kv_length_offsets[src_idxs]
13961405
self.request_query_lengths[dst_idxs] = self.request_query_lengths[src_idxs]
13971406
self.request_output_lengths[dst_idxs] = self.request_output_lengths[src_idxs]
1398-
self.request_metadata[dst_idxs] = self.request_metadata[src_idxs]
13991407
self.request_ids[dst_idxs] = self.request_ids[src_idxs]
14001408
next_tokens[dst_idxs] = next_tokens[src_idxs]
14011409

@@ -1404,6 +1412,9 @@ def _move_book_keeping_tensors(self, src_idxs, dst_idxs, next_tokens):
14041412
self.request_last_kv_block_id[dst_idxs] = self.request_last_kv_block_id[src_idxs]
14051413
self.request_last_kv_block_offset[dst_idxs] = self.request_last_kv_block_offset[src_idxs]
14061414

1415+
for metadata_tensor in self.request_metadata.values():
1416+
metadata_tensor[dst_idxs] = metadata_tensor[src_idxs]
1417+
14071418
if self.is_hybrid_model:
14081419
self.mamba_metadata.request_to_mamba_state_idx[dst_idxs] = (
14091420
self.mamba_metadata.request_to_mamba_state_idx[src_idxs]
@@ -1416,14 +1427,16 @@ def _swap_book_keeping_tensors(self, src_idxs, dst_idxs, next_tokens):
14161427
tensor_swap(self.request_kv_length_offsets, src_idxs, dst_idxs)
14171428
tensor_swap(self.request_query_lengths, src_idxs, dst_idxs)
14181429
tensor_swap(self.request_output_lengths, src_idxs, dst_idxs)
1419-
tensor_swap(self.request_metadata, src_idxs, dst_idxs)
14201430
tensor_swap(self.request_ids, src_idxs, dst_idxs)
14211431
tensor_swap(next_tokens, src_idxs, dst_idxs)
14221432
tensor_swap(self.request_to_kv_block_ids, src_idxs, dst_idxs)
14231433
tensor_swap(self.request_kv_block_counts, src_idxs, dst_idxs)
14241434
tensor_swap(self.request_last_kv_block_id, src_idxs, dst_idxs)
14251435
tensor_swap(self.request_last_kv_block_offset, src_idxs, dst_idxs)
14261436

1437+
for metadata_tensor in self.request_metadata.values():
1438+
tensor_swap(metadata_tensor, src_idxs, dst_idxs)
1439+
14271440
if self.is_hybrid_model:
14281441
tensor_swap(self.mamba_metadata.request_to_mamba_state_idx, src_idxs, dst_idxs)
14291442

megatron/core/inference/inference_request.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import warnings
77
from dataclasses import asdict, dataclass, field
88
from enum import Enum, auto
9-
from typing import Any, Dict, List, Optional
9+
from typing import Any, Dict, List, Optional, Tuple
1010

1111
import torch
1212

@@ -313,20 +313,34 @@ def tracked_metadata(self) -> List[Any]:
313313
"in its sampling_params. Defaulting to -1."
314314
)
315315
sp.termination_id = -1
316-
return [getattr(sp, field) for field in self.get_metadata_labels().keys()]
316+
return [getattr(sp, field) for field, _, _ in self.get_metadata_types()]
317317

318318
@staticmethod
319-
def get_metadata_labels() -> Dict[str, int]:
320-
"""Provides human-readable labels for the tracked metadata fields."""
321-
ret = [
322-
"temperature",
323-
"top_k",
324-
"top_p",
325-
"termination_id",
326-
"return_log_probs",
327-
"skip_prompt_log_probs",
319+
def get_metadata_types() -> List[Tuple[str, torch.dtype, bool]]:
320+
"""Keeps track of all request metadata names, dtypes, and target device.
321+
322+
Returns:
323+
List[Tuple[str, torch.dtype, bool]]: Mapping from metadata name to:
324+
name (str) - The name of the metadata field.
325+
dtype (torch.dtype) - The datatype of the metadata.
326+
on_device (bool) - Whether the metadata lives on GPU (True) or CPU (False).
327+
"""
328+
# return [
329+
# ("temperature", torch.float32, True),
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+
# ]
336+
return [
337+
("temperature", torch.float32, True),
338+
("top_k", torch.float32, True), # CPU for torch sampling
339+
("top_p", torch.float32, True), # CPU for torch sampling
340+
("termination_id", torch.float32, True),
341+
("return_log_probs", torch.float32, True), # CPU for non-selective logprobs
342+
("skip_prompt_log_probs", torch.float32, True), # CPU for non-selective logprobs
328343
]
329-
return {k: v for v, k in enumerate(ret)}
330344

331345
def add_event(self, type: DynamicInferenceEventType, payload: Optional[Any] = None) -> None:
332346
"""Add event."""

megatron/core/inference/text_generation_controllers/text_generation_controller.py

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@
2323
MaxSequenceLengthOverflowError,
2424
WarmupEngineMode,
2525
)
26-
from megatron.core.inference.inference_request import (
27-
DynamicInferenceRequest,
28-
InferenceRequest,
29-
Status,
30-
)
26+
from megatron.core.inference.inference_request import InferenceRequest, Status
3127
from megatron.core.inference.model_inference_wrappers.abstract_model_inference_wrapper import (
3228
AbstractModelInferenceWrapper,
3329
)
@@ -573,11 +569,7 @@ def _dynamic_step_forward_logits(self, input_ids: Tensor, position_ids: Tensor)
573569
return logits
574570

575571
def _dynamic_step_sample_bookkeeping(
576-
self,
577-
*,
578-
backend: str = "torch",
579-
request_metadata: Optional[Tensor] = None,
580-
request_metadata_labels: Dict[str, int] = None,
572+
self, *, backend: str = "torch", request_metadata: Optional[Dict[str, Tensor]] = None
581573
):
582574
"""Perform bookkeeping necessary to sample logits for dynamic batching.
583575
@@ -586,48 +578,62 @@ def _dynamic_step_sample_bookkeeping(
586578
587579
Args:
588580
backend (str): The sampling backend to use.
589-
request_metadata (Optional[Tensor]): An override for the tensor that manages all
590-
request metadata, such as sampling parameters. By default, this metadata is
591-
retrieved from the context.
592-
request_metadata_labels (Optional[Dict]): An override for the map of metadata labels
593-
to their index in the request_metadata tensor. By default, this metadata is
594-
retrieved from the request object.
581+
request_metadata (Optional[Dict[str, Tensor]]): An override for the tensors
582+
that manage request metadata, such as sampling parameters. By default, this
583+
metadata is retrieved from the context.
595584
"""
596585
assert backend in ["torch"]
597586
context = self.inference_wrapped_model.inference_context
587+
active_request_count = context.total_request_count - context.paused_request_count
598588

599589
if request_metadata is None:
600-
request_metadata = context.request_metadata[
601-
context.paused_request_count : context.total_request_count, :
602-
]
603-
if request_metadata_labels is None:
604-
request_metadata_labels = DynamicInferenceRequest.get_metadata_labels()
605-
active_request_count = request_metadata.size(0)
606-
607-
# Shorthand these, because the torch backend needs them.
608-
temp = request_metadata[:, request_metadata_labels["temperature"]]
609-
top_k = request_metadata[:, request_metadata_labels["top_k"]]
610-
top_p = request_metadata[:, request_metadata_labels["top_p"]]
590+
request_metadata = {
591+
label: tensor[context.paused_request_count : context.total_request_count]
592+
for label, tensor in context.request_metadata.items()
593+
}
611594

612595
# Copy data into relevant tensors.
613-
self.temperature_cuda[:active_request_count].copy_(temp, non_blocking=True)
614-
self.top_k_cuda[:active_request_count] = top_k.to(
596+
# self.temperature_cuda[:active_request_count].copy_(temp, non_blocking=True)
597+
# self.top_k_cuda[:active_request_count] = top_k.to(
598+
# dtype=torch.int32, copy=True, non_blocking=True
599+
# )
600+
# self.top_p_cuda[:active_request_count].copy_(top_p, non_blocking=True)
601+
# self.termination_id_cuda[:active_request_count] = request_metadata[
602+
# :, request_metadata_labels["termination_id"]
603+
# ].to(dtype=torch.int64, copy=True, non_blocking=True)
604+
# self.return_log_probs_cuda[:active_request_count] = request_metadata[
605+
# :, request_metadata_labels["return_log_probs"]
606+
# ].to(dtype=torch.bool, copy=True, non_blocking=True)
607+
# self.skip_prompt_log_probs_cuda[:active_request_count] = request_metadata[
608+
# :, request_metadata_labels["skip_prompt_log_probs"]
609+
# ].to(dtype=torch.bool, copy=True, non_blocking=True)
610+
self.temperature_cuda[:active_request_count].copy_(
611+
request_metadata["temperature"], non_blocking=True
612+
)
613+
self.top_k_cuda[:active_request_count] = request_metadata["top_k"].to(
615614
dtype=torch.int32, copy=True, non_blocking=True
616615
)
617-
self.top_p_cuda[:active_request_count].copy_(top_p, non_blocking=True)
618-
self.termination_id_cuda[:active_request_count] = request_metadata[
619-
:, request_metadata_labels["termination_id"]
620-
].to(dtype=torch.int64, copy=True, non_blocking=True)
621-
self.return_log_probs_cuda[:active_request_count] = request_metadata[
622-
:, request_metadata_labels["return_log_probs"]
623-
].to(dtype=torch.bool, copy=True, non_blocking=True)
616+
self.top_p_cuda[:active_request_count].copy_(request_metadata["top_p"], non_blocking=True)
617+
self.termination_id_cuda[:active_request_count] = request_metadata["termination_id"].to(
618+
dtype=torch.int64, copy=True, non_blocking=True
619+
)
620+
self.return_log_probs_cuda[:active_request_count] = request_metadata["return_log_probs"].to(
621+
dtype=torch.bool, copy=True, non_blocking=True
622+
)
624623
self.skip_prompt_log_probs_cuda[:active_request_count] = request_metadata[
625-
:, request_metadata_labels["skip_prompt_log_probs"]
624+
"skip_prompt_log_probs"
626625
].to(dtype=torch.bool, copy=True, non_blocking=True)
627626

628627
if backend == "torch":
629628
# Bucketize the core sampling parameters.
630-
core_params = torch.stack((temp, top_k, top_p), dim=1)
629+
core_params = torch.stack(
630+
(
631+
request_metadata["temperature"],
632+
request_metadata["top_k"],
633+
request_metadata["top_p"],
634+
),
635+
dim=1,
636+
)
631637
_, inv_indices, cnts = torch.unique(
632638
core_params, dim=0, return_inverse=True, return_counts=True
633639
)

tests/unit_tests/inference/text_generation_controllers/test_simple_text_generation_controller.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,16 +258,11 @@ def test_sample_from_dynamic_logits(self, backend):
258258
rev_sampling_dict[idx] = sampling_params
259259

260260
# Prepare metadata for sample bookkeeping.
261-
request_metadata_labels = DynamicInferenceRequest.get_metadata_labels()
262-
request_metadata = torch.empty(
263-
(batch_size, len(request_metadata_labels)), dtype=torch.float32
264-
).cuda()
265-
top_k_values = torch.Tensor([s.top_k for s in rev_sampling_dict]).cuda()
266-
request_metadata[:, request_metadata_labels["top_k"]] = top_k_values
267-
top_p_values = torch.Tensor([s.top_p for s in rev_sampling_dict]).cuda()
268-
request_metadata[:, request_metadata_labels["top_p"]] = top_p_values
269-
temp_values = torch.Tensor([s.temperature for s in rev_sampling_dict]).cuda()
270-
request_metadata[:, request_metadata_labels["temperature"]] = temp_values
261+
request_metadata = {
262+
"temperature": torch.Tensor([s.temperature for s in rev_sampling_dict]).cuda(),
263+
"top_k": torch.Tensor([s.top_k for s in rev_sampling_dict]).cuda(),
264+
"top_p": torch.Tensor([s.top_p for s in rev_sampling_dict]).cuda(),
265+
}
271266

272267
# Bookkeeping.
273268
self.text_generation_controller._dynamic_step_sample_bookkeeping(

0 commit comments

Comments
 (0)