已合并
perf(inductor): finalize planned NPU fast launch submission #45508
Erwinnn创建于 8 天前
perf(inductor): finalize planned NPU fast launch submission #45508
已合并
Erwinnn创建于 8 天前
4 个文件变更+286-60
@@ -2,11 +2,20 @@ from .types import FastLaunchError
2 2 
3 3 
4def __getattr__(name):4def __getattr__(name):
5- if name in ("BoundFastLaunch", "bind_python_wrapper_kernel_fast"):5+ if name in (
6- from .bind import BoundFastLaunch, bind_python_wrapper_kernel_fast6+ "BoundFastLaunch",
7+ "FinalizedFastLaunch",
8+ "bind_python_wrapper_kernel_fast",
9+ ):
10+ from .bind import (
11+ BoundFastLaunch,
12+ FinalizedFastLaunch,
13+ bind_python_wrapper_kernel_fast,
14+ )
7 15 
8 return {16 return {
9 "BoundFastLaunch": BoundFastLaunch,17 "BoundFastLaunch": BoundFastLaunch,
18+ "FinalizedFastLaunch": FinalizedFastLaunch,
10 "bind_python_wrapper_kernel_fast": bind_python_wrapper_kernel_fast,19 "bind_python_wrapper_kernel_fast": bind_python_wrapper_kernel_fast,
11 }[name]20 }[name]
12 raise AttributeError(name)21 raise AttributeError(name)
@@ -14,6 +23,7 @@ def __getattr__(name):
14 23 
15__all__ = [24__all__ = [
16 "BoundFastLaunch",25 "BoundFastLaunch",
26+ "FinalizedFastLaunch",
17 "FastLaunchError",27 "FastLaunchError",
18 "bind_python_wrapper_kernel_fast",28 "bind_python_wrapper_kernel_fast",
19]29]
@@ -1,5 +1,6 @@
1from __future__ import annotations1from __future__ import annotations
2 2 
3+import ast
3from importlib import import_module4from importlib import import_module
4from numbers import Real5from numbers import Real
5from operator import index as operator_index6from operator import index as operator_index
@@ -84,6 +85,17 @@ def _normalize_grid(grid: Any) -> tuple[int, int, int]:
84 return values85 return values
85 86 
86 87 
88+def _constant_grid(launcher: Any) -> tuple[int, int, int] | None:
89+ exprs = tuple(getattr(launcher, "_npu_fast_launch_grid_exprs", ()) or ())
90+ if len(exprs) != 3:
91+ return None
92+ try:
93+ values = tuple(operator_index(ast.literal_eval(expr)) for expr in exprs)
94+ except (SyntaxError, ValueError, TypeError):
95+ return None
96+ return _normalize_grid(values)
97+ 
98+ 
87def _load_c_extension() -> Any:99def _load_c_extension() -> Any:
88 try:100 try:
89 return import_module("torch_npu._C")101 return import_module("torch_npu._C")
@@ -183,10 +195,14 @@ def _validate_runtime_arg_categories(
183class PlannedFastLaunch:195class PlannedFastLaunch:
184 __slots__ = (196 __slots__ = (
185 "arg_kinds",197 "arg_kinds",
198+ "fixed_args",
186 "get_grid",199 "get_grid",
187 "launcher",200 "launcher",
188 "plan",201 "plan",
202+ "runtime_arg_count",
203+ "static_grid",
189 "untimed_launch",204 "untimed_launch",
205+ "untimed_static_launch",
190 )206 )
191 207 
192 def __init__(208 def __init__(
@@ -195,14 +211,22 @@ class PlannedFastLaunch:
195 launcher: Any,211 launcher: Any,
196 plan: Any,212 plan: Any,
197 arg_kinds: tuple[str, ...],213 arg_kinds: tuple[str, ...],
214+ runtime_arg_count: int,
215+ fixed_args: tuple[Any, ...],
198 get_grid: Callable[..., Any],216 get_grid: Callable[..., Any],
199 untimed_launch: Callable[..., Any],217 untimed_launch: Callable[..., Any],
218+ static_grid: tuple[int, int, int] | None,
219+ untimed_static_launch: Callable[..., Any] | None,
200 ) -> None:220 ) -> None:
201 self.launcher = launcher221 self.launcher = launcher
202 self.plan = plan222 self.plan = plan
203 self.arg_kinds = arg_kinds223 self.arg_kinds = arg_kinds
224+ self.runtime_arg_count = runtime_arg_count
225+ self.fixed_args = fixed_args
204 self.get_grid = get_grid226 self.get_grid = get_grid
205 self.untimed_launch = untimed_launch227 self.untimed_launch = untimed_launch
228+ self.static_grid = static_grid
229+ self.untimed_static_launch = untimed_static_launch
206 230 
207 def __call__(231 def __call__(
208 self,232 self,
@@ -210,9 +234,9 @@ class PlannedFastLaunch:
210 *,234 *,
211 stream: Any,235 stream: Any,
212 ) -> None:236 ) -> None:
213- if len(args) != len(self.arg_kinds):237+ if len(args) != self.runtime_arg_count:
214 raise FastLaunchError(238 raise FastLaunchError(
215- f"args_size_mismatch:{len(args)}:{len(self.arg_kinds)}",239+ f"args_size_mismatch:{len(args)}:{self.runtime_arg_count}",
216 backend_submitted=False,240 backend_submitted=False,
217 stable=True,241 stable=True,
218 )242 )
@@ -221,25 +245,32 @@ class PlannedFastLaunch:
221 "stream_is_none",245 "stream_is_none",
222 backend_submitted=False,246 backend_submitted=False,
223 )247 )
224- try:248+ grid = self.static_grid
225- grid = _normalize_grid(self.get_grid(*args))249+ if grid is None:
226- except FastLaunchError:250+ try:
227- raise251+ grid = _normalize_grid(self.get_grid(*args, *self.fixed_args))
228- except Exception as exc:252+ except FastLaunchError:
229- raise FastLaunchError(253+ raise
230- f"grid_resolve_error:{type(exc).__name__}",254+ except Exception as exc:
231- backend_submitted=False,255+ raise FastLaunchError(
232- ) from exc256+ f"grid_resolve_error:{type(exc).__name__}",
257+ backend_submitted=False,
258+ ) from exc
233 259 
234 try:260 try:
235- self.untimed_launch(261+ if self.static_grid is not None:
236- self.plan,262+ if self.untimed_static_launch is None:
237- stream,263+ raise RuntimeError("static fast launch entry is unavailable")
238- grid[0],264+ self.untimed_static_launch(self.plan, stream, args)
239- grid[1],265+ else:
240- grid[2],266+ self.untimed_launch(
241- args,267+ self.plan,
242- )268+ stream,
269+ grid[0],
270+ grid[1],
271+ grid[2],
272+ args,
273+ )
243 except Exception as exc:274 except Exception as exc:
244 # All recoverable validation is completed before entering C++.275 # All recoverable validation is completed before entering C++.
245 # Treat errors after the boundary as submitted so fallback can never276 # Treat errors after the boundary as submitted so fallback can never
@@ -296,6 +327,13 @@ def build_planned_fast_launch(
296 _validate_callsite_schema(callsite_metadata, arg_kinds, runtime_arg_count)327 _validate_callsite_schema(callsite_metadata, arg_kinds, runtime_arg_count)
297 if canonical_args is not None:328 if canonical_args is not None:
298 _validate_runtime_arg_categories(canonical_args, arg_kinds)329 _validate_runtime_arg_categories(canonical_args, arg_kinds)
330+ if canonical_args is None:
331+ canonical_args = ()
332+ fixed_args = tuple(canonical_args[runtime_arg_count:])
333+ if any(
334+ kind == "tensor" for kind in arg_kinds[runtime_arg_count:]
335+ ):
336+ raise FastLaunchPlanUnavailable("fixed_tensor_arg_unsupported")
299 337 
300 extension = _load_c_extension()338 extension = _load_c_extension()
301 make_plan = getattr(extension, "_npu_inductor_make_fast_launch_plan", None)339 make_plan = getattr(extension, "_npu_inductor_make_fast_launch_plan", None)
@@ -303,6 +341,15 @@ def build_planned_fast_launch(
303 if not callable(make_plan) or not callable(launch):341 if not callable(make_plan) or not callable(launch):
304 raise FastLaunchPlanUnavailable("planned_backend_unavailable")342 raise FastLaunchPlanUnavailable("planned_backend_unavailable")
305 343 
344+ static_grid = _constant_grid(launcher)
345+ static_launch = getattr(
346+ extension,
347+ "_npu_inductor_fast_launch_static_with_plan",
348+ None,
349+ )
350+ if static_grid is not None and not callable(static_launch):
351+ static_grid = None
352+ 
306 enable_simt = bool(getattr(launcher, "_npu_fast_launch_enable_simt", False))353 enable_simt = bool(getattr(launcher, "_npu_fast_launch_enable_simt", False))
307 shared_mem_dynamic_size = int(354 shared_mem_dynamic_size = int(
308 getattr(launcher, "_npu_fast_launch_shared_mem_dynamic_size", 0) or 0355 getattr(launcher, "_npu_fast_launch_shared_mem_dynamic_size", 0) or 0
@@ -317,6 +364,9 @@ def build_planned_fast_launch(
317 shared_mem_dynamic_size,364 shared_mem_dynamic_size,
318 is_pure_simt,365 is_pure_simt,
319 bool(target_support_ffts),366 bool(target_support_ffts),
367+ runtime_arg_count,
368+ fixed_args,
369+ static_grid or (),
320 )370 )
321 # The C++ plan owns the stub object; this additional reference owns the371 # The C++ plan owns the stub object; this additional reference owns the
322 # loaded binary that produced it.372 # loaded binary that produced it.
@@ -330,8 +380,12 @@ def build_planned_fast_launch(
330 launcher=launcher,380 launcher=launcher,
331 plan=plan,381 plan=plan,
332 arg_kinds=arg_kinds,382 arg_kinds=arg_kinds,
383+ runtime_arg_count=runtime_arg_count,
384+ fixed_args=fixed_args,
333 get_grid=get_grid,385 get_grid=get_grid,
334 untimed_launch=launch,386 untimed_launch=launch,
387+ static_grid=static_grid,
388+ untimed_static_launch=static_launch,
335 )389 )
336 390 
337 391 
@@ -76,15 +76,23 @@ class BoundFastLaunch:
76 "autotuner",76 "autotuner",
77 "metadata",77 "metadata",
78 "_direct",78 "_direct",
79+ "_call_slot",
79 "_negative_callable",80 "_negative_callable",
80 "_negative_launcher",81 "_negative_launcher",
81 "_static_full_entry_reason",82 "_static_full_entry_reason",
82 )83 )
83 84 
84- def __init__(self, autotuner: Any, metadata: dict[str, Any]) -> None:85+ def __init__(
86+ self,
87+ autotuner: Any,
88+ metadata: dict[str, Any],
89+ *,
90+ call_slot: list[Any] | None = None,
91+ ) -> None:
85 self.autotuner = autotuner92 self.autotuner = autotuner
86 self.metadata = dict(metadata)93 self.metadata = dict(metadata)
87 self._direct: PlannedFastLaunch | None = None94 self._direct: PlannedFastLaunch | None = None
95+ self._call_slot = call_slot
88 self._static_full_entry_reason = _static_full_entry_reason(autotuner)96 self._static_full_entry_reason = _static_full_entry_reason(autotuner)
89 self._negative_launcher: Any | None = None97 self._negative_launcher: Any | None = None
90 self._negative_callable: Any | None = None98 self._negative_callable: Any | None = None
@@ -124,6 +132,8 @@ class BoundFastLaunch:
124 self._direct = None132 self._direct = None
125 self._negative_launcher = launcher133 self._negative_launcher = launcher
126 self._negative_callable = launcher134 self._negative_callable = launcher
135+ if self._call_slot is not None:
136+ self._call_slot[0] = self
127 137 
128 def _try_promote(self, args: tuple[Any, ...]) -> bool:138 def _try_promote(self, args: tuple[Any, ...]) -> bool:
129 launcher = self._stable_launcher()139 launcher = self._stable_launcher()
@@ -150,6 +160,8 @@ class BoundFastLaunch:
150 self._install_negative(launcher)160 self._install_negative(launcher)
151 return False161 return False
152 self._clear_negative()162 self._clear_negative()
163+ if self._call_slot is not None:
164+ self._call_slot[0] = FinalizedFastLaunch(self, self._direct)
153 return True165 return True
154 166 
155 def _fallback(167 def _fallback(
@@ -211,9 +223,8 @@ class BoundFastLaunch:
211 args,223 args,
212 stream=stream,224 stream=stream,
213 )225 )
214- canonical_args = self._canonical_args(args)
215 try:226 try:
216- direct(canonical_args, stream=stream)227+ direct(args, stream=stream)
217 except FastLaunchError as exc:228 except FastLaunchError as exc:
218 if exc.backend_submitted:229 if exc.backend_submitted:
219 raise230 raise
@@ -284,6 +295,57 @@ class BoundFastLaunch:
284 return result295 return result
285 296 
286 297 
298+class FinalizedFastLaunch:
299+ """Steady-state entry installed directly into a generated call slot."""
300+ 
301+ __slots__ = ("bound", "direct")
302+ 
303+ def __init__(self, bound: BoundFastLaunch, direct: PlannedFastLaunch) -> None:
304+ self.bound = bound
305+ self.direct = direct
306+ 
307+ def __call__(
308+ self,
309+ *args: Any,
310+ stream: Any,
311+ benchmark_run: bool = False,
312+ **kwargs: Any,
313+ ) -> Any:
314+ if (
315+ benchmark_run
316+ or kwargs
317+ or autograd_profiler._is_profiler_enabled
318+ or getattr(self.bound.autotuner, "best_launcher", None)
319+ is not self.direct.launcher
320+ ):
321+ return self.bound(
322+ *args,
323+ stream=stream,
324+ benchmark_run=benchmark_run,
325+ **kwargs,
326+ )
327+ if _launcher_has_active_launch_hooks(self.direct.launcher):
328+ return self.bound._hook_fallback(
329+ self.direct.launcher,
330+ args,
331+ stream=stream,
332+ )
333+ try:
334+ self.direct(args, stream=stream)
335+ except FastLaunchError as exc:
336+ if exc.backend_submitted:
337+ raise
338+ if exc.stable:
339+ self.bound._install_negative(self.direct.launcher)
340+ return self.bound._fallback(
341+ args,
342+ stream=stream,
343+ benchmark_run=False,
344+ kwargs={},
345+ )
346+ return None
347+ 
348+ 
287def bind_python_wrapper_kernel_fast(349def bind_python_wrapper_kernel_fast(
288 metadata: dict[str, Any],350 metadata: dict[str, Any],
289 autotuner: Any,351 autotuner: Any,
@@ -293,11 +355,15 @@ def bind_python_wrapper_kernel_fast(
293 bound = (355 bound = (
294 autotuner.run356 autotuner.run
295 if _is_grouped_autotuner(autotuner)357 if _is_grouped_autotuner(autotuner)
296- else BoundFastLaunch(autotuner, metadata)358+ else BoundFastLaunch(autotuner, metadata, call_slot=call_slot)
297 )359 )
298 if call_slot is not None:360 if call_slot is not None:
299 call_slot[0] = bound361 call_slot[0] = bound
300 return bound362 return bound
301 363 
302 364 
303-__all__ = ["BoundFastLaunch", "bind_python_wrapper_kernel_fast"]365+__all__ = [
366+ "BoundFastLaunch",
367+ "FinalizedFastLaunch",
368+ "bind_python_wrapper_kernel_fast",
369+]
@@ -46,6 +46,7 @@ struct FastLaunchPlan {
46 void* kernelStub = nullptr;46 void* kernelStub = nullptr;
47 std::vector<FastLaunchArgKind> argKinds;47 std::vector<FastLaunchArgKind> argKinds;
48 std::vector<FastLaunchArgLayout> argLayouts;48 std::vector<FastLaunchArgLayout> argLayouts;
49+ size_t runtimeArgCount = 0;
49 size_t fftsOffset = 0;50 size_t fftsOffset = 0;
50 size_t gridOffsets[3] = {0, 0, 0};51 size_t gridOffsets[3] = {0, 0, 0};
51 size_t packedArgsSize = 0;52 size_t packedArgsSize = 0;
@@ -54,6 +55,9 @@ struct FastLaunchPlan {
54 bool isPureSimt = false;55 bool isPureSimt = false;
55 bool targetSupportFfts = false;56 bool targetSupportFfts = false;
56 void* fftsAddress = nullptr;57 void* fftsAddress = nullptr;
58+ std::vector<uint8_t> packedArgsTemplate;
59+ uint32_t staticBlockNum = 0;
60+ bool hasStaticGrid = false;
57};61};
58 62 
59size_t AlignOffset(size_t offset, size_t alignment) {63size_t AlignOffset(size_t offset, size_t alignment) {
@@ -277,23 +281,7 @@ struct PackedLaunch {
277 rtStream_t stream = nullptr;281 rtStream_t stream = nullptr;
278};282};
279 283 
280-PackedLaunch PackLaunch(284+uint32_t ValidateGrid(uint32_t grid0, uint32_t grid1, uint32_t grid2) {
281- const FastLaunchPlan& plan,
282- uint64_t streamValue,
283- uint32_t grid0,
284- uint32_t grid1,
285- uint32_t grid2,
286- const py::sequence& args) {
287- size_t argCount = static_cast<size_t>(py::len(args));
288- TORCH_CHECK(
289- argCount == plan.argKinds.size(),
290- "fast launch args and arg_kinds size mismatch: ",
291- argCount,
292- " vs ",
293- plan.argKinds.size());
294- rtStream_t stream = reinterpret_cast<rtStream_t>(streamValue);
295- TORCH_CHECK(stream != nullptr, "fast launch stream pointer is null");
296- 
297 const uint32_t grid[3] = {grid0, grid1, grid2};285 const uint32_t grid[3] = {grid0, grid1, grid2};
298 uint64_t blockNum = 1;286 uint64_t blockNum = 1;
299 for (size_t index = 0; index < 3; ++index) {287 for (size_t index = 0; index < 3; ++index) {
@@ -307,30 +295,80 @@ PackedLaunch PackLaunch(
307 blockNum <= std::numeric_limits<uint16_t>::max(),295 blockNum <= std::numeric_limits<uint16_t>::max(),
308 "fast launch grid product exceeds uint16 max");296 "fast launch grid product exceeds uint16 max");
309 }297 }
298+ return static_cast<uint32_t>(blockNum);
299+}
310 300 
311- PackedLaunch packed;301+void WriteGrid(
312- packed.blockNum = static_cast<uint32_t>(blockNum);302+ const FastLaunchPlan& plan,
313- packed.stream = stream;303+ std::vector<uint8_t>& args,
314- TORCH_INTERNAL_ASSERT(plan.argLayouts.size() == argCount);304+ uint32_t grid0,
315- packed.args.resize(plan.packedArgsSize, 0);305+ uint32_t grid1,
316- if (plan.targetSupportFfts) {306+ uint32_t grid2) {
317- WritePointerAt(packed.args, plan.fftsOffset, plan.fftsAddress);307+ const int32_t signedGrid[3] = {
318- }
319- for (size_t index = 0; index < argCount; ++index) {
320- WriteArgAt(packed.args, args[index], plan.argLayouts[index]);
321- }
322- int32_t signedGrid[3] = {
323 static_cast<int32_t>(grid0),308 static_cast<int32_t>(grid0),
324 static_cast<int32_t>(grid1),309 static_cast<int32_t>(grid1),
325 static_cast<int32_t>(grid2),310 static_cast<int32_t>(grid2),
326 };311 };
327 for (size_t index = 0; index < 3; ++index) {312 for (size_t index = 0; index < 3; ++index) {
328 WriteBytesAt(313 WriteBytesAt(
329- packed.args,314+ args,
330 plan.gridOffsets[index],315 plan.gridOffsets[index],
331 &signedGrid[index],316 &signedGrid[index],
332 sizeof(signedGrid[index]));317 sizeof(signedGrid[index]));
333 }318 }
319+}
320+ 
321+PackedLaunch PackLaunch(
322+ const FastLaunchPlan& plan,
323+ uint64_t streamValue,
324+ uint32_t grid0,
325+ uint32_t grid1,
326+ uint32_t grid2,
327+ const py::sequence& args) {
328+ size_t argCount = static_cast<size_t>(py::len(args));
329+ TORCH_CHECK(
330+ argCount == plan.runtimeArgCount,
331+ "fast launch args and arg_kinds size mismatch: ",
332+ argCount,
333+ " vs ",
334+ plan.runtimeArgCount);
335+ rtStream_t stream = reinterpret_cast<rtStream_t>(streamValue);
336+ TORCH_CHECK(stream != nullptr, "fast launch stream pointer is null");
337+ 
338+ PackedLaunch packed;
339+ packed.blockNum = ValidateGrid(grid0, grid1, grid2);
340+ packed.stream = stream;
341+ TORCH_INTERNAL_ASSERT(plan.argLayouts.size() >= argCount);
342+ packed.args = plan.packedArgsTemplate;
343+ for (size_t index = 0; index < argCount; ++index) {
344+ WriteArgAt(packed.args, args[index], plan.argLayouts[index]);
345+ }
346+ WriteGrid(plan, packed.args, grid0, grid1, grid2);
347+ return packed;
348+}
349+ 
350+PackedLaunch PackStaticLaunch(
351+ const FastLaunchPlan& plan,
352+ uint64_t streamValue,
353+ const py::sequence& args) {
354+ TORCH_CHECK(plan.hasStaticGrid, "fast launch plan has no static grid");
355+ size_t argCount = static_cast<size_t>(py::len(args));
356+ TORCH_CHECK(
357+ argCount == plan.runtimeArgCount,
358+ "fast launch args and arg_kinds size mismatch: ",
359+ argCount,
360+ " vs ",
361+ plan.runtimeArgCount);
362+ rtStream_t stream = reinterpret_cast<rtStream_t>(streamValue);
363+ TORCH_CHECK(stream != nullptr, "fast launch stream pointer is null");
364+ 
365+ PackedLaunch packed;
366+ packed.blockNum = plan.staticBlockNum;
367+ packed.stream = stream;
368+ packed.args = plan.packedArgsTemplate;
369+ for (size_t index = 0; index < argCount; ++index) {
370+ WriteArgAt(packed.args, args[index], plan.argLayouts[index]);
371+ }
334 return packed;372 return packed;
335}373}
336 374 
@@ -364,8 +402,9 @@ void SubmitLaunch(const FastLaunchPlan& plan, PackedLaunch packed) {
364 return static_cast<int>(result);402 return static_cast<int>(result);
365 };403 };
366 404 
367- at_npu::native::OpCommand command;405+ // The launch callable is fully prepared. Reuse the existing OpAPI V2 queue
368- command.Name(plan.kernelName).SetCustomHandler(std::move(launchCall)).Run();406+ // entry instead of rebuilding a generic zero-I/O OpCommand for every hit.
407+ at_npu::native::OpCommand::RunOpApiV2(plan.kernelName, launchCall);
369}408}
370 409 
371std::shared_ptr<FastLaunchPlan> MakeFastLaunchPlan(410std::shared_ptr<FastLaunchPlan> MakeFastLaunchPlan(
@@ -375,7 +414,10 @@ std::shared_ptr<FastLaunchPlan> MakeFastLaunchPlan(
375 bool enableSimt,414 bool enableSimt,
376 uint64_t sharedMemDynamicSize,415 uint64_t sharedMemDynamicSize,
377 bool isPureSimt,416 bool isPureSimt,
378- bool targetSupportFfts) {417+ bool targetSupportFfts,
418+ size_t runtimeArgCount,
419+ const py::sequence& fixedArgs,
420+ const std::vector<uint32_t>& staticGrid) {
379 TORCH_CHECK(421 TORCH_CHECK(
380 sharedMemDynamicSize <= std::numeric_limits<uint32_t>::max(),422 sharedMemDynamicSize <= std::numeric_limits<uint32_t>::max(),
381 "shared_mem_dynamic_size exceeds uint32 max");423 "shared_mem_dynamic_size exceeds uint32 max");
@@ -386,6 +428,17 @@ std::shared_ptr<FastLaunchPlan> MakeFastLaunchPlan(
386 plan->kernelStubOwner = kernelStub;428 plan->kernelStubOwner = kernelStub;
387 plan->kernelStub = ExtractPointer(kernelStub, "kernel_stub");429 plan->kernelStub = ExtractPointer(kernelStub, "kernel_stub");
388 plan->argKinds = ParseArgKinds(argKinds);430 plan->argKinds = ParseArgKinds(argKinds);
431+ if (runtimeArgCount == std::numeric_limits<size_t>::max()) {
432+ runtimeArgCount = plan->argKinds.size();
433+ }
434+ TORCH_CHECK(
435+ runtimeArgCount <= plan->argKinds.size(),
436+ "runtime arg count exceeds fast launch ABI size");
437+ TORCH_CHECK(
438+ static_cast<size_t>(py::len(fixedArgs)) ==
439+ plan->argKinds.size() - runtimeArgCount,
440+ "fixed fast launch args do not complete the ABI");
441+ plan->runtimeArgCount = runtimeArgCount;
389 plan->enableSimt = enableSimt;442 plan->enableSimt = enableSimt;
390 plan->sharedMemDynamicSize = sharedMemDynamicSize;443 plan->sharedMemDynamicSize = sharedMemDynamicSize;
391 plan->isPureSimt = isPureSimt;444 plan->isPureSimt = isPureSimt;
@@ -404,6 +457,32 @@ std::shared_ptr<FastLaunchPlan> MakeFastLaunchPlan(
404 plan->fftsAddress = reinterpret_cast<void*>(fftsAddress);457 plan->fftsAddress = reinterpret_cast<void*>(fftsAddress);
405 }458 }
406 BuildPackedLayout(*plan);459 BuildPackedLayout(*plan);
460+ plan->packedArgsTemplate.resize(plan->packedArgsSize, 0);
461+ if (plan->targetSupportFfts) {
462+ WritePointerAt(
463+ plan->packedArgsTemplate, plan->fftsOffset, plan->fftsAddress);
464+ }
465+ for (size_t index = runtimeArgCount; index < plan->argKinds.size(); ++index) {
466+ TORCH_CHECK(
467+ plan->argKinds[index] != FastLaunchArgKind::Tensor,
468+ "fixed tensor fast launch arguments are unsupported");
469+ WriteArgAt(
470+ plan->packedArgsTemplate,
471+ fixedArgs[index - runtimeArgCount],
472+ plan->argLayouts[index]);
473+ }
474+ if (!staticGrid.empty()) {
475+ TORCH_CHECK(staticGrid.size() == 3, "static fast launch grid must have rank 3");
476+ plan->staticBlockNum =
477+ ValidateGrid(staticGrid[0], staticGrid[1], staticGrid[2]);
478+ WriteGrid(
479+ *plan,
480+ plan->packedArgsTemplate,
481+ staticGrid[0],
482+ staticGrid[1],
483+ staticGrid[2]);
484+ plan->hasStaticGrid = true;
485+ }
407 return plan;486 return plan;
408}487}
409 488 
@@ -418,6 +497,14 @@ void FastLaunchWithPlan(
418 SubmitLaunch(*plan, PackLaunch(*plan, stream, grid0, grid1, grid2, args));497 SubmitLaunch(*plan, PackLaunch(*plan, stream, grid0, grid1, grid2, args));
419}498}
420 499 
500+void FastLaunchStaticWithPlan(
501+ const std::shared_ptr<FastLaunchPlan>& plan,
502+ uint64_t stream,
503+ const py::sequence& args) {
504+ TORCH_CHECK(plan != nullptr, "fast launch plan is null");
505+ SubmitLaunch(*plan, PackStaticLaunch(*plan, stream, args));
506+}
507+ 
421} // namespace508} // namespace
422 509 
423void RegisterNPUFastLaunchBindings(PyObject* module) {510void RegisterNPUFastLaunchBindings(PyObject* module) {
@@ -433,7 +520,10 @@ void RegisterNPUFastLaunchBindings(PyObject* module) {
433 py::arg("enable_simt") = false,520 py::arg("enable_simt") = false,
434 py::arg("shared_mem_dynamic_size") = 0,521 py::arg("shared_mem_dynamic_size") = 0,
435 py::arg("is_pure_simt") = false,522 py::arg("is_pure_simt") = false,
436- py::arg("target_support_ffts") = false);523+ py::arg("target_support_ffts") = false,
524+ py::arg("runtime_arg_count") = std::numeric_limits<size_t>::max(),
525+ py::arg("fixed_args") = py::tuple(),
526+ py::arg("static_grid") = std::vector<uint32_t>());
437 m.def(527 m.def(
438 "_npu_inductor_fast_launch_with_plan",528 "_npu_inductor_fast_launch_with_plan",
439 &FastLaunchWithPlan,529 &FastLaunchWithPlan,
@@ -443,6 +533,12 @@ void RegisterNPUFastLaunchBindings(PyObject* module) {
443 py::arg("grid_1"),533 py::arg("grid_1"),
444 py::arg("grid_2"),534 py::arg("grid_2"),
445 py::arg("args"));535 py::arg("args"));
536+ m.def(
537+ "_npu_inductor_fast_launch_static_with_plan",
538+ &FastLaunchStaticWithPlan,
539+ py::arg("plan"),
540+ py::arg("stream"),
541+ py::arg("args"));
446}542}
447 543 
448#endif544#endif