@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 848562bd49
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 14, 05:30 UTC

break execute_model in gpu_model_runner into sub-functions for custom scopes (#24265)

Co-authored-by: Bangsheng Tang <bangsheng@meta.com>

Bangsheng Tang committed Sep 6, 2025 at 14:02 UTC 848562bd49a91d21f2dc76912d5fcb25ae80c8a8
3 files changed +207 -108
vllm/envs.py
+4
@@ -168,6 +168,7 @@ if TYPE_CHECKING:
168 VLLM_ALLREDUCE_USE_SYMM_MEM: bool = False
169 VLLM_TUNED_CONFIG_FOLDER: Optional[str] = None
170 VLLM_DISABLE_PAD_FOR_CUDAGRAPH: bool = False
171 + VLLM_CUSTOM_SCOPES_FOR_PROFILING: bool = False
172
173
174 def get_default_cache_root():
@@ -1200,6 +1201,9 @@ environment_variables: dict[str, Callable[[], Any]] = {
1201 "VLLM_TUNED_CONFIG_FOLDER":
1202 lambda: os.getenv("VLLM_TUNED_CONFIG_FOLDER", None),
1203
1204 + # Add optional custom scopes for profiling, disable to avoid overheads
1205 + "VLLM_CUSTOM_SCOPES_FOR_PROFILING":
1206 + lambda: bool(int(os.getenv("VLLM_CUSTOM_SCOPES_FOR_PROFILING", "0"))),
1207 }
1208
1209 # --8<-- [end:env-vars-definition]
vllm/v1/utils.py
+15 -4
@@ -1,17 +1,21 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import argparse
4 +import contextlib
5 import multiprocessing
6 import time
7 import weakref
8 from collections.abc import Sequence
9 +from contextlib import AbstractContextManager
10 from multiprocessing import connection
11 from multiprocessing.process import BaseProcess
12 from typing import (TYPE_CHECKING, Any, Callable, Generic, Optional, TypeVar,
13 Union, overload)
14
15 import torch
16 +from torch.autograd.profiler import record_function
17
18 +import vllm.envs as envs
19 from vllm.logger import init_logger
20 from vllm.usage.usage_lib import (UsageContext, is_usage_stats_enabled,
21 usage_message)
@@ -155,7 +159,7 @@ def get_engine_client_zmq_addr(local_only: bool,
159
160 class APIServerProcessManager:
161 """Manages a group of API server processes.
158 -
162 +
163 Handles creation, monitoring, and termination of API server worker
164 processes. Also monitors extra processes to check if they are healthy.
165 """
@@ -172,7 +176,7 @@ class APIServerProcessManager:
176 stats_update_address: Optional[str] = None,
177 ):
178 """Initialize and start API server worker processes.
175 -
179 +
180 Args:
181 target_server_fn: Function to call for each API server process
182 listen_address: Address to listen for client connections
@@ -181,7 +185,7 @@ class APIServerProcessManager:
185 num_servers: Number of API server processes to start
186 input_addresses: Input addresses for each API server
187 output_addresses: Output addresses for each API server
184 - stats_update_address: Optional stats update address
188 + stats_update_address: Optional stats update address
189 """
190 self.listen_address = listen_address
191 self.sock = sock
@@ -225,7 +229,7 @@ def wait_for_completion_or_failure(
229 "CoreEngineActorManager"]] = None,
230 coordinator: Optional["DPCoordinator"] = None) -> None:
231 """Wait for all processes to complete or detect if any fail.
228 -
232 +
233 Raises an exception if any process exits with a non-zero status.
234
235 Args:
@@ -368,3 +372,10 @@ def report_usage_stats(
372 "disable_custom_all_reduce":
373 vllm_config.parallel_config.disable_custom_all_reduce,
374 })
375 +
376 +
377 +def record_function_or_nullcontext(name: str) -> AbstractContextManager:
378 + if envs.VLLM_CUSTOM_SCOPES_FOR_PROFILING:
379 + return record_function(name)
380 + else:
381 + return contextlib.nullcontext()
vllm/v1/worker/gpu_model_runner.py
+188 -104
@@ -69,7 +69,8 @@ from vllm.v1.kv_cache_interface import (AttentionSpec,
69 KVCacheGroupSpec, KVCacheSpec,
70 MambaSpec, SlidingWindowSpec)
71 from vllm.v1.outputs import (EMPTY_MODEL_RUNNER_OUTPUT, AsyncModelRunnerOutput,
72 - DraftTokenIds, LogprobsTensors, ModelRunnerOutput)
72 + DraftTokenIds, LogprobsLists, LogprobsTensors,
73 + ModelRunnerOutput, SamplerOutput)
74 from vllm.v1.pool.metadata import PoolingMetadata
75 from vllm.v1.sample.logits_processor import LogitsProcessors, build_logitsprocs
76 from vllm.v1.sample.metadata import SamplingMetadata
@@ -79,7 +80,7 @@ from vllm.v1.spec_decode.eagle import EagleProposer
80 from vllm.v1.spec_decode.medusa import MedusaProposer
81 from vllm.v1.spec_decode.metadata import SpecDecodeMetadata
82 from vllm.v1.spec_decode.ngram_proposer import NgramProposer
82 -from vllm.v1.utils import CpuGpuBuffer
83 +from vllm.v1.utils import CpuGpuBuffer, record_function_or_nullcontext
84 from vllm.v1.worker.gpu_input_batch import CachedRequestState, InputBatch
85 from vllm.v1.worker.kv_connector_model_runner_mixin import (
86 KVConnectorModelRunnerMixin, KVConnectorOutput)
@@ -1587,31 +1588,13 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1588 kv_connector_output=kv_connector_output,
1589 )
1590
1590 - @torch.inference_mode()
1591 - def execute_model(
1591 + def _preprocess(
1592 self,
1593 scheduler_output: "SchedulerOutput",
1594 intermediate_tensors: Optional[IntermediateTensors] = None,
1595 - ) -> Union[ModelRunnerOutput, AsyncModelRunnerOutput, IntermediateTensors]:
1596 - self._update_states(scheduler_output)
1597 - if not scheduler_output.total_num_scheduled_tokens:
1598 - if not has_kv_transfer_group():
1599 - # Return empty ModelRunnerOutput if there's no work to do.
1600 - return EMPTY_MODEL_RUNNER_OUTPUT
1601 -
1602 - return self.kv_connector_no_forward(scheduler_output,
1603 - self.vllm_config)
1604 -
1605 - if self.cache_config.kv_sharing_fast_prefill:
1606 - assert not self.input_batch.num_prompt_logprobs, (
1607 - "--kv-sharing-fast-prefill produces incorrect logprobs for "
1608 - "prompt tokens, tokens, please disable it when the requests "
1609 - "need prompt logprobs")
1610 -
1611 - # Prepare the decoder inputs.
1612 - (attn_metadata, logits_indices, spec_decode_metadata,
1613 - num_scheduled_tokens_np, spec_decode_common_attn_metadata,
1614 - max_query_len) = self._prepare_inputs(scheduler_output)
1595 + ) -> tuple[int, int, Optional[torch.Tensor], Optional[torch.Tensor],
1596 + Optional[torch.Tensor], torch.Tensor,
1597 + Optional[IntermediateTensors], dict[str, Any]]:
1598
1599 num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens
1600 if (self.compilation_config.cudagraph_mode != CUDAGraphMode.NONE
@@ -1683,75 +1666,21 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1666 intermediate_tensors = self.sync_and_slice_intermediate_tensors(
1667 num_input_tokens, intermediate_tensors, True)
1668
1686 - uniform_decode = (max_query_len == self.uniform_decode_query_len) and (
1687 - num_scheduled_tokens == self.input_batch.num_reqs * max_query_len)
1688 - batch_descriptor = BatchDescriptor(num_tokens=num_input_tokens,
1689 - uniform_decode=uniform_decode)
1690 - cudagraph_runtime_mode, batch_descriptor = \
1691 - self.cudagraph_dispatcher.dispatch(batch_descriptor)
1692 -
1693 - # Run the model.
1694 - # Use persistent buffers for CUDA graphs.
1695 - with set_forward_context(
1696 - attn_metadata,
1697 - self.vllm_config,
1698 - num_tokens=num_input_tokens,
1699 - num_tokens_across_dp=num_tokens_across_dp,
1700 - cudagraph_runtime_mode=cudagraph_runtime_mode,
1701 - batch_descriptor=batch_descriptor,
1702 - ), self.maybe_get_kv_connector_output(
1703 - scheduler_output) as kv_connector_output:
1704 -
1705 - model_output = self.model(
1706 - input_ids=input_ids,
1707 - positions=positions,
1708 - intermediate_tensors=intermediate_tensors,
1709 - inputs_embeds=inputs_embeds,
1710 - **model_kwargs,
1711 - )
1712 -
1713 - if self.use_aux_hidden_state_outputs:
1714 - hidden_states, aux_hidden_states = model_output
1715 - else:
1716 - hidden_states = model_output
1717 - aux_hidden_states = None
1718 -
1719 - # Broadcast PP output for external_launcher (torchrun)
1720 - # to make sure we are synced across pp ranks
1721 - # TODO: Support overlapping mirco-batches
1722 - # https://github.com/vllm-project/vllm/issues/18019
1723 - broadcast_pp_output = \
1724 - self.parallel_config.distributed_executor_backend \
1725 - == "external_launcher" and len(get_pp_group().ranks) > 0
1726 - if not get_pp_group().is_last_rank:
1727 - # For mid-pipeline stages, return the hidden states.
1728 - assert isinstance(hidden_states, IntermediateTensors)
1729 - if not broadcast_pp_output:
1730 - hidden_states.kv_connector_output = kv_connector_output
1731 - return hidden_states
1732 - get_pp_group().send_tensor_dict(hidden_states.tensors,
1733 - all_gather_group=get_tp_group())
1734 - logits = None
1735 - else:
1736 - if self.is_pooling_model:
1737 - return self._pool(hidden_states, num_scheduled_tokens,
1738 - num_scheduled_tokens_np, kv_connector_output)
1739 -
1740 - sample_hidden_states = hidden_states[logits_indices]
1741 - logits = self.model.compute_logits(sample_hidden_states, None)
1742 - if broadcast_pp_output:
1743 - model_output_broadcast_data = {
1744 - "logits": logits.contiguous(),
1745 - } if logits is not None else {}
1746 - model_output_broadcast_data = get_pp_group().broadcast_tensor_dict(
1747 - model_output_broadcast_data, src=len(get_pp_group().ranks) - 1)
1748 - assert model_output_broadcast_data is not None
1749 - logits = model_output_broadcast_data["logits"]
1750 -
1751 - # Apply structured output bitmasks if present
1752 - if scheduler_output.grammar_bitmask is not None:
1753 - self.apply_grammar_bitmask(scheduler_output, logits)
1669 + return (
1670 + num_scheduled_tokens,
1671 + num_input_tokens,
1672 + num_tokens_across_dp,
1673 + input_ids,
1674 + inputs_embeds,
1675 + positions,
1676 + intermediate_tensors,
1677 + model_kwargs,
1678 + )
1679
1680 + def _sample(
1681 + self, logits: Optional[torch.Tensor],
1682 + spec_decode_metadata: Optional[SpecDecodeMetadata]
1683 + ) -> SamplerOutput:
1684 # Sample the next token and get logprobs if needed.
1685 sampling_metadata = self.input_batch.sampling_metadata
1686 if spec_decode_metadata is None:
@@ -1785,6 +1714,21 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1714 )
1715 sampler_output.sampled_token_ids = output_token_ids
1716
1717 + return sampler_output
1718 +
1719 + def _bookkeeping_sync(
1720 + self, scheduler_output: "SchedulerOutput",
1721 + sampler_output: SamplerOutput, logits: Optional[torch.Tensor],
1722 + hidden_states: torch.Tensor, num_scheduled_tokens: int
1723 + ) -> tuple[
1724 + dict[str, int],
1725 + Optional[LogprobsLists],
1726 + list[list[int]],
1727 + dict[str, Optional[LogprobsTensors]],
1728 + list[str],
1729 + dict[str, int],
1730 + list[int],
1731 + ]:
1732 num_nans_in_logits = {}
1733 if envs.VLLM_COMPUTE_NANS_IN_LOGITS:
1734 num_nans_in_logits = self._get_nans_in_logits(logits)
@@ -1827,6 +1771,7 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1771
1772 num_sampled_tokens = sampler_output.sampled_token_ids.shape[0]
1773 sampled_token_ids = sampler_output.sampled_token_ids
1774 + invalid_req_indices = []
1775 if not self.use_async_scheduling:
1776 # Get the valid generated tokens.
1777 max_gen_len = sampled_token_ids.shape[-1]
@@ -1892,20 +1837,159 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1837 req_state = self.requests[req_id]
1838 req_state.output_token_ids.extend(sampled_ids)
1839
1840 + return (
1841 + num_nans_in_logits,
1842 + logprobs_lists,
1843 + valid_sampled_token_ids,
1844 + prompt_logprobs_dict,
1845 + req_ids_output_copy,
1846 + req_id_to_index_output_copy,
1847 + invalid_req_indices,
1848 + )
1849 +
1850 + @torch.inference_mode()
1851 + def execute_model(
1852 + self,
1853 + scheduler_output: "SchedulerOutput",
1854 + intermediate_tensors: Optional[IntermediateTensors] = None,
1855 + ) -> Union[ModelRunnerOutput, AsyncModelRunnerOutput, IntermediateTensors]:
1856 + with record_function_or_nullcontext("Preprocess"):
1857 + self._update_states(scheduler_output)
1858 + if not scheduler_output.total_num_scheduled_tokens:
1859 + if not has_kv_transfer_group():
1860 + # Return empty ModelRunnerOutput if there's no work to do.
1861 + return EMPTY_MODEL_RUNNER_OUTPUT
1862 + return self.kv_connector_no_forward(scheduler_output,
1863 + self.vllm_config)
1864 + if self.cache_config.kv_sharing_fast_prefill:
1865 + assert not self.input_batch.num_prompt_logprobs, (
1866 + "--kv-sharing-fast-prefill produces incorrect logprobs for "
1867 + "prompt tokens, tokens, please disable it when the requests"
1868 + " need prompt logprobs")
1869 +
1870 + # Prepare the decoder inputs.
1871 + (attn_metadata, logits_indices, spec_decode_metadata,
1872 + num_scheduled_tokens_np, spec_decode_common_attn_metadata,
1873 + max_query_len) = self._prepare_inputs(scheduler_output)
1874 +
1875 + (
1876 + num_scheduled_tokens,
1877 + num_input_tokens,
1878 + num_tokens_across_dp,
1879 + input_ids,
1880 + inputs_embeds,
1881 + positions,
1882 + intermediate_tensors,
1883 + model_kwargs,
1884 + ) = self._preprocess(scheduler_output, intermediate_tensors)
1885 +
1886 + uniform_decode = (max_query_len
1887 + == self.uniform_decode_query_len) and (
1888 + num_scheduled_tokens
1889 + == self.input_batch.num_reqs * max_query_len)
1890 + batch_descriptor = BatchDescriptor(num_tokens=num_input_tokens,
1891 + uniform_decode=uniform_decode)
1892 + cudagraph_runtime_mode, batch_descriptor = \
1893 + self.cudagraph_dispatcher.dispatch(batch_descriptor)
1894 +
1895 + # Run the model.
1896 + # Use persistent buffers for CUDA graphs.
1897 + with (set_forward_context(
1898 + attn_metadata,
1899 + self.vllm_config,
1900 + num_tokens=num_input_tokens,
1901 + num_tokens_across_dp=num_tokens_across_dp,
1902 + cudagraph_runtime_mode=cudagraph_runtime_mode,
1903 + batch_descriptor=batch_descriptor,
1904 + ), record_function_or_nullcontext("Forward"),
1905 + self.maybe_get_kv_connector_output(scheduler_output) as
1906 + kv_connector_output):
1907 + model_output = self.model(
1908 + input_ids=input_ids,
1909 + positions=positions,
1910 + intermediate_tensors=intermediate_tensors,
1911 + inputs_embeds=inputs_embeds,
1912 + **model_kwargs,
1913 + )
1914 +
1915 + with record_function_or_nullcontext("Postprocess"):
1916 + if self.use_aux_hidden_state_outputs:
1917 + hidden_states, aux_hidden_states = model_output
1918 + else:
1919 + hidden_states = model_output
1920 + aux_hidden_states = None
1921 +
1922 + # Broadcast PP output for external_launcher (torchrun)
1923 + # to make sure we are synced across pp ranks
1924 + # TODO: Support overlapping mirco-batches
1925 + # https://github.com/vllm-project/vllm/issues/18019
1926 + broadcast_pp_output = \
1927 + self.parallel_config.distributed_executor_backend \
1928 + == "external_launcher" and len(get_pp_group().ranks) > 0
1929 + if not get_pp_group().is_last_rank:
1930 + # For mid-pipeline stages, return the hidden states.
1931 + assert isinstance(hidden_states, IntermediateTensors)
1932 + if not broadcast_pp_output:
1933 + hidden_states.kv_connector_output = kv_connector_output
1934 + return hidden_states
1935 + get_pp_group().send_tensor_dict(
1936 + hidden_states.tensors, all_gather_group=get_tp_group())
1937 + logits = None
1938 + else:
1939 + if self.is_pooling_model:
1940 + return self._pool(hidden_states, num_scheduled_tokens,
1941 + num_scheduled_tokens_np,
1942 + kv_connector_output)
1943 +
1944 + sample_hidden_states = hidden_states[logits_indices]
1945 + logits = self.model.compute_logits(sample_hidden_states, None)
1946 + if broadcast_pp_output:
1947 + model_output_broadcast_data = {
1948 + "logits": logits.contiguous(),
1949 + } if logits is not None else {}
1950 + model_output_broadcast_data = get_pp_group(
1951 + ).broadcast_tensor_dict(model_output_broadcast_data,
1952 + src=len(get_pp_group().ranks) - 1)
1953 + assert model_output_broadcast_data is not None
1954 + logits = model_output_broadcast_data["logits"]
1955 +
1956 + # Apply structured output bitmasks if present
1957 + if scheduler_output.grammar_bitmask is not None:
1958 + self.apply_grammar_bitmask(scheduler_output, logits)
1959 +
1960 + with record_function_or_nullcontext("Sample"):
1961 + sampler_output = self._sample(logits, spec_decode_metadata)
1962 +
1963 + with record_function_or_nullcontext("Bookkeep"):
1964 + assert isinstance(hidden_states, torch.Tensor)
1965 + (
1966 + num_nans_in_logits,
1967 + logprobs_lists,
1968 + valid_sampled_token_ids,
1969 + prompt_logprobs_dict,
1970 + req_ids_output_copy,
1971 + req_id_to_index_output_copy,
1972 + invalid_req_indices,
1973 + ) = self._bookkeeping_sync(scheduler_output, sampler_output,
1974 + logits, hidden_states,
1975 + num_scheduled_tokens)
1976 +
1977 if self.speculative_config:
1978 assert spec_decode_common_attn_metadata is not None
1897 - self._draft_token_ids = self.propose_draft_token_ids(
1898 - scheduler_output,
1899 - valid_sampled_token_ids,
1900 - sampling_metadata,
1901 - hidden_states,
1902 - sample_hidden_states,
1903 - aux_hidden_states,
1904 - spec_decode_metadata,
1905 - spec_decode_common_attn_metadata,
1906 - )
1979 + with record_function_or_nullcontext("Draft"):
1980 + self._draft_token_ids = self.propose_draft_token_ids(
1981 + scheduler_output,
1982 + valid_sampled_token_ids,
1983 + self.input_batch.sampling_metadata,
1984 + hidden_states,
1985 + sample_hidden_states,
1986 + aux_hidden_states,
1987 + spec_decode_metadata,
1988 + spec_decode_common_attn_metadata,
1989 + )
1990
1908 - self.eplb_step()
1991 + with record_function_or_nullcontext("EPLB"):
1992 + self.eplb_step()
1993
1994 output = ModelRunnerOutput(
1995 req_ids=req_ids_output_copy,
@@ -1923,7 +2007,7 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2007
2008 return AsyncGPUModelRunnerOutput(
2009 model_runner_output=output,
1926 - sampled_token_ids=sampled_token_ids,
2010 + sampled_token_ids=sampler_output.sampled_token_ids,
2011 invalid_req_indices=invalid_req_indices,
2012 async_output_copy_stream=self.async_output_copy_stream,
2013 )