From a1946c9f61f92c7b42e507a3a7782d6bc1c9c90f Mon Sep 17 00:00:00 2001 From: dongbo910220 <32610838+dongbo910220@users.noreply.github.com> Date: Sun, 19 Oct 2025 03:12:01 +0800 Subject: [PATCH] [Chore] Separate out profiling utilities from vllm.utils (#27150) Signed-off-by: dongbo910220 <1275604947@qq.com> --- docs/contributing/profiling.md | 14 ++++--- vllm/utils/__init__.py | 69 ++++++++++++---------------------- vllm/utils/profiling.py | 56 +++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 50 deletions(-) create mode 100644 vllm/utils/profiling.py diff --git a/docs/contributing/profiling.md b/docs/contributing/profiling.md index d12872e95a26c..fed286f4b6343 100644 --- a/docs/contributing/profiling.md +++ b/docs/contributing/profiling.md @@ -180,9 +180,13 @@ The profiling traces generated by the continuous profiling workflow are publicly The Python standard library includes [cProfile](https://docs.python.org/3/library/profile.html) for profiling Python code. vLLM includes a couple of helpers that make it easy to apply it to a section of vLLM. -Both the `vllm.utils.cprofile` and `vllm.utils.cprofile_context` functions can be +Both the `vllm.utils.profiling.cprofile` and `vllm.utils.profiling.cprofile_context` functions can be used to profile a section of code. +!!! note + The legacy import paths `vllm.utils.cprofile` and `vllm.utils.cprofile_context` are deprecated. + Please use `vllm.utils.profiling.cprofile` and `vllm.utils.profiling.cprofile_context` instead. + ### Example usage - decorator The first helper is a Python decorator that can be used to profile a function. @@ -190,9 +194,9 @@ If a filename is specified, the profile will be saved to that file. If no filena specified, profile data will be printed to stdout. ```python -import vllm.utils +from vllm.utils.profiling import cprofile -@vllm.utils.cprofile("expensive_function.prof") +@cprofile("expensive_function.prof") def expensive_function(): # some expensive code pass @@ -204,13 +208,13 @@ The second helper is a context manager that can be used to profile a block of code. Similar to the decorator, the filename is optional. ```python -import vllm.utils +from vllm.utils.profiling import cprofile_context def another_function(): # more expensive code pass -with vllm.utils.cprofile_context("another_function.prof"): +with cprofile_context("another_function.prof"): another_function() ``` diff --git a/vllm/utils/__init__.py b/vllm/utils/__init__.py index 7cb3805fcbeaa..33690d86da045 100644 --- a/vllm/utils/__init__.py +++ b/vllm/utils/__init__.py @@ -59,6 +59,29 @@ import vllm.envs as envs from vllm.logger import enable_trace_function_call, init_logger from vllm.ray.lazy_utils import is_in_ray_actor +_DEPRECATED_PROFILING = {"cprofile", "cprofile_context"} + + +def __getattr__(name: str) -> Any: # noqa: D401 - short deprecation docstring + """Module-level getattr to handle deprecated profiling utilities.""" + if name in _DEPRECATED_PROFILING: + warnings.warn( + f"vllm.utils.{name} is deprecated and will be removed in a future version. " + f"Use vllm.utils.profiling.{name} instead.", + DeprecationWarning, + stacklevel=2, + ) + import vllm.utils.profiling as _prof + + return getattr(_prof, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__() -> list[str]: + # expose deprecated names in dir() for better UX/tab-completion + return sorted(list(globals().keys()) + list(_DEPRECATED_PROFILING)) + + if TYPE_CHECKING: from argparse import Namespace @@ -1412,51 +1435,7 @@ def warn_for_unimplemented_methods(cls: type[T]) -> type[T]: return cls -@contextlib.contextmanager -def cprofile_context(save_file: str | None = None): - """Run a cprofile - - Args: - save_file: path to save the profile result. "1" or - None will result in printing to stdout. - """ - import cProfile - - prof = cProfile.Profile() - prof.enable() - - try: - yield - finally: - prof.disable() - if save_file and save_file != "1": - prof.dump_stats(save_file) - else: - prof.print_stats(sort="cumtime") - - -def cprofile(save_file: str | None = None, enabled: bool = True): - """Decorator to profile a Python method using cProfile. - - Args: - save_file: Path to save the profile result. - If "1", None, or "", results will be printed to stdout. - enabled: Set to false to turn this into a no-op - """ - - def decorator(func: Callable): - @wraps(func) - def wrapper(*args, **kwargs): - if not enabled: - # If profiling is disabled, just call the function directly. - return func(*args, **kwargs) - - with cprofile_context(save_file): - return func(*args, **kwargs) - - return wrapper - - return decorator +## moved to vllm.utils.profiling (imported at module top) # Only relevant for models using ALiBi (e.g, MPT) diff --git a/vllm/utils/profiling.py b/vllm/utils/profiling.py new file mode 100644 index 0000000000000..b669106939577 --- /dev/null +++ b/vllm/utils/profiling.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project + +from __future__ import annotations + +import contextlib +from collections.abc import Callable +from functools import wraps +from typing import Any + + +@contextlib.contextmanager +def cprofile_context(save_file: str | None = None): + """Run a cprofile + + Args: + save_file: path to save the profile result. "1" or + None will result in printing to stdout. + """ + import cProfile + + prof = cProfile.Profile() + prof.enable() + + try: + yield + finally: + prof.disable() + if save_file and save_file != "1": + prof.dump_stats(save_file) + else: + prof.print_stats(sort="cumtime") + + +def cprofile(save_file: str | None = None, enabled: bool = True): + """Decorator to profile a Python method using cProfile. + + Args: + save_file: Path to save the profile result. + If "1", None, or "", results will be printed to stdout. + enabled: Set to false to turn this into a no-op + """ + + def decorator(func: Callable): + @wraps(func) + def wrapper(*args: Any, **kwargs: Any): + if not enabled: + # If profiling is disabled, just call the function directly. + return func(*args, **kwargs) + + with cprofile_context(save_file): + return func(*args, **kwargs) + + return wrapper + + return decorator