Skip to content

Commit 232c2bb

Browse files
committed
Implement FlashInfer sampling backend
1 parent 16a93e7 commit 232c2bb

7 files changed

Lines changed: 313 additions & 112 deletions

File tree

megatron/core/inference/config.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
from dataclasses import InitVar, dataclass
44
from enum import Enum
5-
from typing import List, Optional, Tuple
5+
from typing import List, Literal, Optional, Tuple
66

77
import torch
88

9+
from megatron.core.inference.inference_request import DynamicInferenceRequest
910
from megatron.core.process_groups_config import ProcessGroupCollection
1011
from megatron.core.transformer.module import MegatronModule
1112
from megatron.core.utils import get_attr_wrapped_model
@@ -297,6 +298,9 @@ class InferenceConfig:
297298
Defaults to 0, which means no logging.
298299
"""
299300

301+
sampling_backend: Literal['torch', 'flashinfer'] = 'torch'
302+
"""Which sampling kernels to use during inference."""
303+
300304
request_metadata_types: Optional[List[Tuple[str, torch.dtype, bool]]] = None
301305
"""
302306
A list of the per-request metadata types to track. Each entry is a tuple
@@ -320,3 +324,16 @@ def __post_init__(self, verbose: bool):
320324
f"prefix_caching_routing_alpha must be in [0, 1], "
321325
f"got {self.prefix_caching_routing_alpha}"
322326
)
327+
328+
if self.sampling_backend == 'flashinfer':
329+
try:
330+
import flashinfer # noqa: F401
331+
except ImportError:
332+
raise ValueError(
333+
"sampling_backend='flashinfer' requires flashinfer to be installed."
334+
)
335+
336+
if self.request_metadata_types is None:
337+
self.request_metadata_types = DynamicInferenceRequest.get_metadata_types(
338+
sampling_backend=self.sampling_backend
339+
)

megatron/core/inference/contexts/dynamic_context.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,21 @@ def pad_active_slices(self):
11291129
self.token_to_local_position_within_kv_block[padding_token_slice] = 0
11301130
self.token_to_position_in_request[padding_token_slice] = 0
11311131

1132+
# Pad request-level sampling metadata for FlashInfer. Padded slots
1133+
# must contain disabled-filter sentinels so the kernel doesn't trip on
1134+
# stale values. The last-token gather also reads padded slots when
1135+
# materialize_only_last_token_logits is False; index 0 is always
1136+
# in-bounds for the logits buffer.
1137+
if self.config.sampling_backend == 'flashinfer':
1138+
n = self.total_request_count - self.paused_request_count
1139+
padded_n = self.padded_active_request_count
1140+
if padded_n > n:
1141+
self.active_request_metadata["temperature"][n:padded_n].fill_(1.0)
1142+
self.active_request_metadata["top_k"][n:padded_n].fill_(0)
1143+
self.active_request_metadata["top_p"][n:padded_n].fill_(0.0)
1144+
if not self.config.materialize_only_last_token_logits:
1145+
self.active_request_last_token_idxs[n:padded_n].fill_(0)
1146+
11321147
def append_key_value_cache(self, layer_number: int, key: Tensor, value: Tensor) -> None:
11331148
"""Append to KV cache.
11341149
@@ -2229,7 +2244,8 @@ def add_request(
22292244

22302245
# Handle request metadata.
22312246
assert (
2232-
req.get_metadata_types() == self.request_metadata_types
2247+
req.get_metadata_types(sampling_backend=self.config.sampling_backend)
2248+
== self.request_metadata_types
22332249
), "Request added to context with invalid metadata types"
22342250
metadata = req.tracked_metadata
22352251
metadata_types = req.get_metadata_types()

megatron/core/inference/engines/dynamic_engine.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ def create_cuda_graphs(self, reset_context: bool = True):
364364
unwrapped_model = controller.inference_wrapped_model.model
365365
set_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model)
366366

367+
model_config = controller.inference_wrapped_model.model.config
368+
367369
tbar = enumerate(context.cuda_graph_batch_dimensions_list)
368370
if HAVE_TQDM:
369371
tbar = tqdm(tbar, total=len(context.cuda_graph_batch_dimensions_list))
@@ -392,13 +394,14 @@ def create_cuda_graphs(self, reset_context: bool = True):
392394
controller._pre_forward_bookkeeping_stream.wait_stream(
393395
torch.cuda.current_stream()
394396
)
395-
# Launch bookkeeping on a side stream so it overlaps with forward.
396397
with torch.cuda.stream(controller._pre_forward_bookkeeping_stream):
398+
controller._dynamic_step_sample_bookkeeping()
397399
controller._pre_forward_bookkeeping_event.record()
398400

399401
controller._dynamic_step_forward_logits(input_ids, position_ids)
400402

401403
controller._pre_forward_bookkeeping_event.synchronize()
404+
controller._dynamic_step_sample_logits()
402405

403406
context.reset()
404407

megatron/core/inference/inference_request.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -472,24 +472,37 @@ def tracked_metadata(self) -> List[Any]:
472472
return [getattr(sp, field) for field, _, _ in self.get_metadata_types()]
473473

474474
@staticmethod
475-
def get_metadata_types() -> List[Tuple[str, torch.dtype, bool]]:
475+
def get_metadata_types(
476+
sampling_backend: str = 'torch',
477+
) -> List[Tuple[str, torch.dtype, bool]]:
476478
"""Keeps track of all request metadata names, dtypes, and target device.
477479
480+
Args:
481+
sampling_backend: Which sampling backend is in use.
482+
FlashInfer needs sampling parameters directly on GPU.
483+
478484
Returns:
479485
List[Tuple[str, torch.dtype, bool]]: Mapping from metadata name to:
480486
name (str) - The name of the metadata field.
481487
dtype (torch.dtype) - The datatype of the metadata.
482488
on_device (bool) - Whether the metadata lives on GPU (True) or CPU (False).
483489
"""
484-
return [
485-
("temperature", torch.float32, False), # CPU for torch sampling
486-
("top_k", torch.int32, False), # CPU for torch sampling
487-
("top_p", torch.float32, False), # CPU for torch sampling
490+
types = [
491+
("temperature", torch.float32, False),
492+
("top_k", torch.int32, False),
493+
("top_p", torch.float32, False),
488494
("termination_id", torch.int64, True),
489-
("return_log_probs", torch.bool, False), # CPU for non-selective logprobs
490-
("skip_prompt_log_probs", torch.bool, False), # CPU for non-selective logprobs
491-
("top_n_logprobs", torch.int32, False), # CPU for torch sampling
495+
("return_log_probs", torch.bool, False),
496+
("skip_prompt_log_probs", torch.bool, False),
497+
("top_n_logprobs", torch.int32, False),
492498
]
499+
if sampling_backend == 'flashinfer':
500+
gpu_fields = {"temperature", "top_k", "top_p"}
501+
types = [
502+
(label, dtype, True if label in gpu_fields else on_gpu)
503+
for label, dtype, on_gpu in types
504+
]
505+
return types
493506

494507
def add_event(
495508
self, type: DynamicInferenceEventType, payload: Optional[Any] = None

0 commit comments

Comments
 (0)