|
3 | 3 | from .graph_compiler_backend import GraphCompilerBackend |
4 | 4 |
|
5 | 5 |
|
6 | | -# Predefined Inductor config templates. |
7 | | -# Each template maps to a set of torch._inductor.config overrides. |
8 | | -# Reference: https://github.com/pytorch/pytorch/blob/main/torch/_inductor/config.py |
9 | | -# |
10 | | -# Note: These are extension to PyTorch's official "mode" concept. |
11 | | -# PyTorch modes: "default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs" |
12 | | -# These templates provide additional config combinations for specific use cases. |
| 6 | +# Predefined Inductor config templates for GraphNet. |
| 7 | +# Each template maps to a set of torch._inductor.config options. |
13 | 8 | _INDUCTOR_CONFIG_TEMPLATES = { |
14 | 9 | "triton": { |
15 | 10 | # Default Triton code generation (Inductor's default behavior). |
|
21 | 16 | # Reference: torch._inductor.config.cpp_wrapper |
22 | 17 | "cpp_wrapper": True, |
23 | 18 | }, |
24 | | - "cutlass": { |
25 | | - # Enable max-autotune to potentially use CUTLASS-based GEMM kernels. |
26 | | - # CUTLASS backend requires separate installation. |
27 | | - # Reference: torch._inductor.config.max_autotune_gemm_backends |
28 | | - "max_autotune": True, |
29 | | - "max_autotune_gemm": True, |
30 | | - "epilogue_fusion": True, |
31 | | - "coordinate_descent_tuning": True, |
32 | | - }, |
33 | | - "aten": { |
34 | | - # Enable autotune fallback to ATen kernels for debugging. |
35 | | - # This causes Inductor to fall back to ATen (eager) kernels |
36 | | - # when autotuning finds them faster. Useful for debugging. |
37 | | - # Reference: torch._inductor.config.autotune_fallback_to_aten |
38 | | - "autotune_fallback_to_aten": True, |
39 | | - }, |
40 | 19 | "cudagraphs": { |
41 | 20 | # Enable CUDA Graphs to reduce kernel launch overhead. |
42 | 21 | # Reference: torch._inductor.config.triton.cudagraphs |
43 | | - # Note: Prefer using mode="reduce-overhead" for official support. |
| 22 | + # NOTE: CUDA Graphs does not support dynamic shapes or graph breaks, so it is |
| 23 | + # highly recommended to use only for inference with fixed input shapes. |
| 24 | + # Prefer using mode="reduce-overhead" for official support. |
44 | 25 | "triton.cudagraphs": True, |
45 | 26 | }, |
46 | 27 | "max_autotune": { |
47 | 28 | # Enable comprehensive autotuning across all backends. |
48 | | - # Equivalent to torch.compile(mode="max-autotune") with extra options. |
49 | 29 | "max_autotune": True, |
50 | 30 | "max_autotune_gemm": True, |
51 | 31 | "coordinate_descent_tuning": True, |
|
59 | 39 | }, |
60 | 40 | "tma": { |
61 | 41 | # Enable persistent matmul kernels with TMA (Tensor Memory Accelerator). |
| 42 | + # Reference: torch._inductor.config.triton.enable_persistent_tma_matmul |
62 | 43 | # NOTE: This config has graceful fallback behavior: |
63 | 44 | # - On NVIDIA H100+ (Hopper, CC >= 9.0): Enables TMA persistent kernels |
64 | 45 | # - On other GPUs (A100, AMD, etc.): Enables non-TMA persistent kernels as fallback |
65 | | - # Reference: torch._inductor.config.triton.enable_persistent_tma_matmul |
66 | 46 | "triton.enable_persistent_tma_matmul": True, |
67 | 47 | }, |
68 | 48 | } |
69 | 49 |
|
70 | | -# Map template names to torch.compile mode strings where applicable. |
71 | | -# Reference: https://pytorch.org/docs/stable/generated/torch.compile.html |
72 | | -_TEMPLATE_TO_COMPILE_MODE = { |
73 | | - "cudagraphs": "reduce-overhead", |
74 | | - "max_autotune": "max-autotune", |
75 | | -} |
76 | | - |
77 | | - |
78 | | -def _set_nested_attr(config_module, key, value): |
79 | | - """Set a possibly nested attribute on a config module. |
80 | | -
|
81 | | - For example, key="triton.cudagraphs" sets config_module.triton.cudagraphs = value. |
82 | | - """ |
83 | | - parts = key.split(".") |
84 | | - obj = config_module |
85 | | - for part in parts[:-1]: |
86 | | - obj = getattr(obj, part) |
87 | | - setattr(obj, parts[-1], value) |
88 | | - |
89 | 50 |
|
90 | 51 | class InductorBackend(GraphCompilerBackend): |
91 | 52 | """Inductor backend with configurable config template selection. |
92 | 53 |
|
93 | 54 | Supported config keys: |
94 | | - template (str): One of "triton", "cpp_wrapper", "cutlass", "aten", |
| 55 | + fullgraph (bool): Whether to compile the entire model into a single graph. |
| 56 | + Defaults to False. Passed to torch.compile. |
| 57 | + dynamic (bool | None): Whether to enable dynamic shape support. |
| 58 | + Defaults to None. Passed to torch.compile. |
| 59 | + graph_net_inductor_config_template (str): One of "triton", "cpp_wrapper", |
95 | 60 | "cudagraphs", "max_autotune", "freezing", "tma". |
96 | | - Applies a predefined set of Inductor config overrides. |
97 | | - Note: These are extensions to PyTorch's official "mode" concept. |
| 61 | + GraphNet predefined Inductor config templates. |
98 | 62 | mode (str): torch.compile mode. One of "default", "reduce-overhead", |
99 | | - "max-autotune", "max-autotune-no-cudagraphs". |
100 | | - If a template implies a mode, that is used unless explicitly overridden. |
101 | | - freezing (bool): Enable/disable model freezing before compilation. |
102 | | - inductor_config (dict): Arbitrary torch._inductor.config overrides. |
103 | | - Keys can be dotted paths (e.g. "triton.cudagraphs"). |
104 | | - These are applied last and override everything else. |
| 63 | + "max-autotune". Passed directly to torch.compile. |
| 64 | + options (dict): Direct torch.compile options dict. |
| 65 | +
|
| 66 | + NOTE: `torch.compile` does not allow `mode` and `options` to be specified together. |
| 67 | + Therefore `graph_net_inductor_config_template` (which provides `options`), |
| 68 | + `mode` and `options` are mutually exclusive with each other. |
105 | 69 |
|
106 | 70 | Reference: |
107 | | - - PyTorch modes: https://pytorch.org/docs/stable/generated/torch.compile.html |
| 71 | + - PyTorch compile: https://pytorch.org/docs/stable/generated/torch.compile.html |
108 | 72 | - Inductor configs: https://github.com/pytorch/pytorch/blob/main/torch/_inductor/config.py |
109 | 73 | """ |
110 | 74 |
|
111 | 75 | def __init__(self, config): |
112 | 76 | super().__init__(config) |
113 | | - self._template = config.get("template", None) |
114 | | - self._mode = config.get("mode", None) |
115 | | - self._freezing = config.get("freezing", None) |
116 | | - self._inductor_config = config.get("inductor_config", {}) |
117 | | - |
118 | | - def _build_inductor_overrides(self): |
119 | | - """Collect all Inductor config overrides from template + explicit config.""" |
120 | | - overrides = {} |
121 | | - |
122 | | - # 1. Apply template defaults |
123 | | - if self._template is not None: |
124 | | - if self._template not in _INDUCTOR_CONFIG_TEMPLATES: |
125 | | - raise ValueError( |
126 | | - f"Unknown Inductor config template: {self._template!r}. " |
127 | | - f"Available templates: {sorted(_INDUCTOR_CONFIG_TEMPLATES.keys())}" |
128 | | - ) |
129 | | - overrides.update(_INDUCTOR_CONFIG_TEMPLATES[self._template]) |
130 | | - |
131 | | - # 2. Apply top-level convenience flags |
132 | | - if self._freezing is not None: |
133 | | - overrides["freezing"] = self._freezing |
| 77 | + self._config_template = config.get("graph_net_inductor_config_template") |
| 78 | + self._mode = config.get("mode") |
| 79 | + self._fullgraph = config.get("fullgraph", False) |
| 80 | + self._dynamic = config.get("dynamic") |
| 81 | + self._options = config.get("options") |
| 82 | + |
| 83 | + # Mutually exclusive: exactly one of template / mode / options can be specified |
| 84 | + provided = [self._config_template, self._mode, self._options] |
| 85 | + if sum(1 for x in provided if x is not None) > 1: |
| 86 | + raise ValueError( |
| 87 | + "Specify exactly one of 'graph_net_inductor_config_template', " |
| 88 | + "'mode', or 'options', not multiple." |
| 89 | + ) |
134 | 90 |
|
135 | | - # 3. Apply explicit inductor_config overrides (highest priority) |
136 | | - overrides.update(self._inductor_config) |
| 91 | + def _build_options(self): |
| 92 | + if self._options: |
| 93 | + return self._options |
137 | 94 |
|
138 | | - return overrides |
| 95 | + if self._config_template: |
| 96 | + if self._config_template not in _INDUCTOR_CONFIG_TEMPLATES: |
| 97 | + raise ValueError( |
| 98 | + f"Unknown config template: {self._config_template!r}. " |
| 99 | + f"Available: {sorted(_INDUCTOR_CONFIG_TEMPLATES.keys())}" |
| 100 | + ) |
| 101 | + return _INDUCTOR_CONFIG_TEMPLATES[self._config_template] |
139 | 102 |
|
140 | | - def _resolve_compile_mode(self): |
141 | | - """Determine the torch.compile mode string.""" |
142 | | - if self._mode is not None: |
143 | | - return self._mode |
144 | | - if self._template in _TEMPLATE_TO_COMPILE_MODE: |
145 | | - return _TEMPLATE_TO_COMPILE_MODE[self._template] |
146 | | - return "default" |
| 103 | + return {} |
147 | 104 |
|
148 | 105 | def __call__(self, model): |
149 | | - import torch._inductor.config as inductor_config |
| 106 | + final_options = self._build_options() |
150 | 107 |
|
151 | | - overrides = self._build_inductor_overrides() |
152 | | - compile_mode = self._resolve_compile_mode() |
153 | | - |
154 | | - if self._template or self._inductor_config: |
| 108 | + if self._config_template is not None or self._options is not None: |
155 | 109 | print( |
156 | | - f"[InductorBackend] template={self._template!r}, mode={compile_mode!r}, " |
157 | | - f"overrides={overrides}", |
| 110 | + f"[InductorBackend] graph_net_inductor_config_template={self._config_template!r}, " |
| 111 | + f"mode={self._mode!r}, options={final_options}", |
158 | 112 | file=sys.stderr, |
159 | 113 | flush=True, |
160 | 114 | ) |
161 | 115 |
|
162 | | - # Apply Inductor config overrides |
163 | | - for key, value in overrides.items(): |
164 | | - _set_nested_attr(inductor_config, key, value) |
165 | | - |
166 | | - return torch.compile(model, backend="inductor", mode=compile_mode) |
| 116 | + return torch.compile( |
| 117 | + model, |
| 118 | + backend="inductor", |
| 119 | + fullgraph=self._fullgraph, |
| 120 | + dynamic=self._dynamic, |
| 121 | + **({"mode": self._mode} if self._mode is not None else {}), |
| 122 | + **({"options": final_options} if final_options else {}), |
| 123 | + ) |
167 | 124 |
|
168 | 125 | def synchronize(self): |
169 | 126 | if torch.cuda.is_available(): |
|
0 commit comments