feat(npu): add MindIE-SD compile fusion and MINDIE attention backend#259
feat(npu): add MindIE-SD compile fusion and MINDIE attention backend#259Chitandaaaaa wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces NPU support and integrates the MindIE-SD attention backend, enabling optimized attention execution and compilation fusion patterns on NPU devices. It also adds a multi-block profiling mechanism for the Qwen Image transformer. The feedback highlights critical improvements for robustness and flexibility: replacing NPU availability checks with specific MindIE-SD library checks to prevent runtime crashes when the library is missing, addressing thread-safety issues with global profiling variables, and providing an environment-variable-based opt-out mechanism for forced model compilation.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| from diffsynth_engine.utils.platform import is_npu_available | ||
|
|
||
|
|
||
| class MindieAttentionBackend(AttentionBackend): | ||
| @staticmethod | ||
| def check_availability() -> None: | ||
| if not is_npu_available(): | ||
| raise RuntimeError("NPU is not available, cannot use MINDIE attention backend") |
There was a problem hiding this comment.
The check_availability method currently checks is_npu_available(), but the backend relies on the mindiesd library which is imported dynamically during the forward pass. If torch_npu is available but mindiesd is not installed, check_availability will pass, but the forward pass will crash with an ImportError. It is safer to check is_mindie_sd_available() instead.
| from diffsynth_engine.utils.platform import is_npu_available | |
| class MindieAttentionBackend(AttentionBackend): | |
| @staticmethod | |
| def check_availability() -> None: | |
| if not is_npu_available(): | |
| raise RuntimeError("NPU is not available, cannot use MINDIE attention backend") | |
| from diffsynth_engine.utils.platform import is_mindie_sd_available | |
| class MindieAttentionBackend(AttentionBackend): | |
| @staticmethod | |
| def check_availability() -> None: | |
| if not is_mindie_sd_available(): | |
| raise RuntimeError("MindIE-SD is not available, cannot use MINDIE attention backend") |
|
|
||
| if attn_type is None: | ||
| attn_type = "sdpa" | ||
| attn_type = "mindie" if is_npu_available() else "sdpa" |
There was a problem hiding this comment.
When attn_type is None (auto-detect), defaulting to "mindie" solely based on is_npu_available() will cause a runtime crash if the mindiesd library is not installed on the NPU system. It is safer to default to "mindie" only if is_mindie_sd_available() is True, falling back to "sdpa" otherwise.
| attn_type = "mindie" if is_npu_available() else "sdpa" | |
| attn_type = "mindie" if is_mindie_sd_available() else "sdpa" |
| from diffsynth_engine.utils import logging | ||
| from diffsynth_engine.utils.constants import MODEL_INDEX_NAME | ||
| from diffsynth_engine.utils.import_utils import LazyImport | ||
| from diffsynth_engine.utils.platform import is_npu_available |
There was a problem hiding this comment.
| _PROFILE_BLOCK_COUNT = 4 | ||
| _blocks_profiled = 0 | ||
| _profile_limit_reached = False | ||
| _multi_block_profiler = None |
There was a problem hiding this comment.
Using global variables (_blocks_profiled, _profile_limit_reached, _multi_block_profiler) to track profiling state during model execution is not thread-safe. In multi-threaded environments (e.g., concurrent inference requests in a web server), this can lead to race conditions, corrupted profiling metrics, or unexpected crashes. Consider using thread-local storage (threading.local()) or passing the profiling state through the context/call stack to ensure thread safety.
| import torch | ||
|
|
||
| from diffsynth_engine.utils.platform import is_mindie_sd_available | ||
|
|
||
|
|
||
| def apply_mindie_sd_compile(model: torch.nn.Module) -> torch.nn.Module: | ||
| """Apply MindIE-SD torch.compile backend fusion patterns. | ||
|
|
||
| Enables RMSNorm, RoPE, AdaLayerNorm, FastGELU, and MulAdd op fusion | ||
| for NPU execution. On non-NPU devices or when mindiesd is unavailable, | ||
| returns the model unchanged. | ||
| """ | ||
| if not is_mindie_sd_available(): | ||
| return model | ||
|
|
||
| from mindiesd.compilation import MindieSDBackend | ||
|
|
||
| return torch.compile(model, backend=MindieSDBackend()) |
There was a problem hiding this comment.
Forcing torch.compile via apply_mindie_sd_compile whenever is_mindie_sd_available() is True leaves no option for the user to run in eager mode. This can be problematic for debugging, avoiding compilation overhead, or handling models that are incompatible with compilation. Consider adding an opt-out mechanism, such as checking an environment variable (e.g., DIFFSYNTH_DISABLE_COMPILE).
| import torch | |
| from diffsynth_engine.utils.platform import is_mindie_sd_available | |
| def apply_mindie_sd_compile(model: torch.nn.Module) -> torch.nn.Module: | |
| """Apply MindIE-SD torch.compile backend fusion patterns. | |
| Enables RMSNorm, RoPE, AdaLayerNorm, FastGELU, and MulAdd op fusion | |
| for NPU execution. On non-NPU devices or when mindiesd is unavailable, | |
| returns the model unchanged. | |
| """ | |
| if not is_mindie_sd_available(): | |
| return model | |
| from mindiesd.compilation import MindieSDBackend | |
| return torch.compile(model, backend=MindieSDBackend()) | |
| import os | |
| import torch | |
| from diffsynth_engine.utils.platform import is_mindie_sd_available | |
| def apply_mindie_sd_compile(model: torch.nn.Module) -> torch.nn.Module: | |
| """Apply MindIE-SD torch.compile backend fusion patterns. | |
| Enables RMSNorm, RoPE, AdaLayerNorm, FastGELU, and MulAdd op fusion | |
| for NPU execution. On non-NPU devices or when mindiesd is unavailable, | |
| returns the model unchanged. | |
| """ | |
| if not is_mindie_sd_available() or os.environ.get("DIFFSYNTH_DISABLE_COMPILE", "0") == "1": | |
| return model | |
| from mindiesd.compilation import MindieSDBackend | |
| return torch.compile(model, backend=MindieSDBackend()) |
Enable torch.compile with MindieSDBackend on model load for NPU, register the mindie attention backend in the v1 registry, and auto-detect SDPA vs MINDIE based on platform availability. Co-authored-by: Cursor <cursoragent@cursor.com>
6583fa7 to
d2ad4d8
Compare
Pipeline now loads transformers via from_config + load_state_dict, so the compile hook on DiffusionModel.from_pretrained never runs. Re-apply it on the shared init path so fusion kernels show up in profiling again. Co-authored-by: Cursor <cursoragent@cursor.com>
| try: | ||
| import torch_npu | ||
|
|
||
| return torch_npu.npu.is_available() |
There was a problem hiding this comment.
建议改成类似FLASH_ATTN_3_AVAILABLE的环境变量而不是每次try import
| attn_metadata: AttentionMetadata | None = None, | ||
| **kwargs, | ||
| ) -> torch.Tensor: | ||
| from mindiesd.layers.flash_attn.attention_forward import attention_forward |
| model.load_state_dict(state_dict, strict=True, assign=True) | ||
| model.to(device=device) | ||
| return model | ||
|
|
|
|
||
| from mindiesd.compilation import MindieSDBackend | ||
|
|
||
| return torch.compile(model, backend=MindieSDBackend()) |
There was a problem hiding this comment.
这里整体torch.compile不会有graph break问题吗?一般实践中我们只compile repeated blocks
| @@ -1 +1 @@ | |||
| import torch | |||
There was a problem hiding this comment.
platform已经是独立目录的话,考虑把这些平台相关的内容完整迁移过去?
|
|
||
| # attention | ||
| attn_type: AttentionType | str = AttentionType.SDPA | ||
| attn_type: AttentionType | str | None = None # None = auto-detect |
Co-authored-by: Cursor <cursoragent@cursor.com>
Pipeline loads transformers via init_transformer, so apply compile only there. Also clear unused platform package re-export. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
This PR adds NPU support on top of the v1 attention registry architecture.
is_npu_available()andis_mindie_sd_available()inutils/platform.py, and route device selection / distributed backend to NPU (npu) and HCCL when available.mindiebackend inregistry.pyand implementmindie_attn.py, which delegates attention computation to MindIE-SD's fused flash attention kernel.--attn-typeis not specified, automatically choosemindieon NPU andsdpaotherwise.platform/npu/compilation.pyand applytorch.compile(backend=MindieSDBackend())inDiffusionModel.from_pretrainedwhen MindIE-SD is available, enabling RMSNorm / RoPE / AdaLayerNorm / FastGELU / MulAdd fusion on NPU.Made with Cursor