main
py 142 lines 4.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from __future__ import annotations
5
6 import functools
7 import hashlib
8 import inspect
9 import json
10 import types
11 from collections.abc import Callable, Generator
12 from contextlib import contextmanager
13 from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar
14
15 import torch
16 from torch import fx
17 from torch._subclasses.fake_tensor import FakeTensorMode, unset_fake_temporarily
18
19 if TYPE_CHECKING:
20 from vllm.config.utils import Range
21
22 from torch._inductor.custom_graph_pass import CustomGraphPass
23
24 _pass_context = None
25 P = ParamSpec("P")
26 R = TypeVar("R")
27
28
29 class PassContext:
30 def __init__(self, compile_range: Range):
31 self.compile_range: Range = compile_range
32
33 # set of arg indices
34 self.donated_input_ids: set[int] = set()
35
36
37 def get_pass_context() -> PassContext:
38 """Get the current pass context."""
39 assert _pass_context is not None
40 return _pass_context
41
42
43 @contextmanager
44 def pass_context(compile_range: Range) -> Generator[None, None, None]:
45 """A context manager that stores the current pass context,
46 usually it is a list of sizes to specialize.
47 """
48 global _pass_context
49 prev_context = _pass_context
50 _pass_context = PassContext(compile_range)
51 try:
52 yield
53 finally:
54 _pass_context = prev_context
55
56
57 @functools.cache
58 def _hash_source_cached(*srcs: str | type | types.FunctionType) -> str:
59 hasher = hashlib.sha256()
60 for src in srcs:
61 src_str = src if isinstance(src, str) else inspect.getsource(src)
62 hasher.update(src_str.encode("utf-8"))
63 return hasher.hexdigest()
64
65
66 class InductorPass(CustomGraphPass): # type: ignore[misc]
67 """A custom graph pass that uses a hash of its source as the UUID.
68 This is defined as a convenience and should work in most cases.
69 """
70
71 def uuid(self) -> str:
72 """Provide a unique identifier for the pass, used in Inductor code cache.
73 This should depend on the pass implementation, so that changes to the
74 pass result in recompilation.
75 By default, the object source is hashed.
76 """
77 return InductorPass.hash_source(self)
78
79 @staticmethod
80 def hash_source(*srcs: str | Any) -> str:
81 """Utility method to hash the sources of functions or objects.
82
83 Args:
84 srcs: strings or objects to add to the hash.
85 Objects and functions have their source inspected.
86 Results are cached by resolved types to avoid repeated
87 inspect.getsource() calls.
88
89 """
90 # Resolve instances to their class for a hashable cache key.
91 cache_key = tuple(
92 src if isinstance(src, (str, type, types.FunctionType)) else src.__class__
93 for src in srcs
94 )
95 return _hash_source_cached(*cache_key)
96
97 @staticmethod
98 def hash_dict(dict_: dict[Any, Any]) -> str:
99 """Utility method to hash a dictionary, can alternatively be used for uuid.
100
101 Returns:
102 A sha256 hash of the json rep of the dictionary.
103
104 """
105 encoded = json.dumps(dict_, sort_keys=True).encode("utf-8")
106 return hashlib.sha256(encoded).hexdigest()
107
108 def is_applicable_for_range(self, compile_range: Range) -> bool:
109 return True
110
111
112 class CallableInductorPass(InductorPass):
113 """This class is a wrapper for a callable that automatically provides an
114 implementation of the UUID.
115 """
116
117 def __init__(
118 self, callable: Callable[[fx.Graph], None], uuid: Any | None = None
119 ) -> None:
120 self.callable = callable
121 self._uuid = self.hash_source(callable) if uuid is None else uuid
122
123 def __call__(self, graph: torch.fx.Graph) -> None:
124 self.callable(graph)
125
126 def uuid(self) -> Any:
127 return self._uuid
128
129
130 def enable_fake_mode(fn: Callable[P, R]) -> Callable[P, R]:
131 """Applies a FakeTensorMode context. This is useful when you don't want to
132 create or run things with real tensors.
133 """
134
135 @functools.wraps(fn)
136 def fn_new(*args: P.args, **kwargs: P.kwargs) -> R:
137 with torch._guards.tracing(None), unset_fake_temporarily(), FakeTensorMode():
138 result = fn(*args, **kwargs)
139
140 return result
141
142 return fn_new