Repository navigation
Conversation
Author
|
Superseded by #265 — my changes have been merged into that branch: Verified on 2 x Ascend950PR (1248x832, 40 steps, reference-image parameters Branch: https://github.com/achena666/DiffSynth-Engine/tree/npu-merge-265 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这个 PR 做了什么
DiffSynth-Engine 目前只能跑在 CUDA 上:设备探测、通信后端、注意力实现、低精度量化四处都绑定了 CUDA 专用接口。本 PR 增加 Ascend NPU 后端,让 Qwen-Image 系列管线可以在昇腾上单卡与多卡运行,并支持 FB-Cache、序列并行、W8A8 量化三个特性。
设计原则:不改变任何现有默认行为。 所有新增分支都以
IS_NPU为条件,CUDA 路径与改动前逐行一致;测试里的新增开关全部有默认值,不设置时等价于上游行为。验证环境:openEuler 24.03 / CANN 9.2.0 / torch 2.8.0+cpu / torch_npu 2.8.0.post5 / transformers 4.53.0。
改动清单
1.
utils/platform.pyIS_NPU探测(torch_npu可导入且torch.npu.is_available()),导入失败时安全降级empty_cache()增加 NPU 分支2.
models/basic/attention.py(核心)npu_ulysses_attention():all_to_all(聚合序列 / 切分 head)→npu_fusion_attention→all_to_all还原npu_sp_gather_attention():ring_degree > 1时的默认路径,把完整 K/Vall_gather到各 rank,Q 保持本地分片npu_ring_attention():真正的环形 P2P 实现(NPU_RING_IMPL=ring启用)long_context_attention()在 NPU 上分发到上面三条路径为什么必须改:
AttnType.TORCH_EFFICIENT指定 SDPA 的 mem-efficient 后端,torch_npu 未实现 → 自动回退 CPU → 输出张量留在 CPU,后续 HCCL 集合通信报create_config ... error code 4/DIST invalid type。这个错误信息把注意力故障伪装成通信故障,很难定位。关于 yunchang 的 NPU ring 实现:
ring_npu_flash_attn_forward用if step <= comm.rank作为计算条件——这是 causal 分支,需要not is_causal守护——并且从不调用update_out_and_lse合并各分块的 softmax 统计量,实际只返回本地分片。实测 rank0 的 ring 输出与「只用本地 KV」的注意力完全相等(max_err=0),整条管线出的图与预期是两张。本 PR 的npu_ring_attention复用 yunchang 的RingComm与update_out_and_lse,只重写循环,数值上与全量注意力一致(seq 64/512/2048,最大误差 0.004,即 bf16 精度)。默认仍用 gather:环形每块先舍入到 bf16 再合并,40 步扩散会使端到端结果变差;且 2 卡下两者总通信量相同(都是
N-1份 KV),环形的收益是计算与通信重叠。可用NPU_RING_IMPL=ring切换。3.
utils/parallel.pynccl→hccl(NPU 时)torch.cuda.set_device/synchronize增加 NPU 分支device_id只传给 NCCL(HCCL 不接受该参数)master_port支持环境变量DSNP_MASTER_PORT,避免端口冲突4.
utils/fp8_linear.py(W8A8 在线量化)npu_quant_linear():激活走npu_dynamic_quant(dst_type=292)(per-token,融合算子),权重离线做 per-channel
float8_e4m3fn,矩阵乘走npu_quant_matmul(fp8 × fp8 → bf16)NPU_QUANT_DTYPE=fp8|int8切换位宽,默认fp8NPU_QUANT_SKIP(默认proj_out,to_out)按模块名保留 bf16 权重weight.npu_quant_scale(Parameter 属性)而非按id(weight)建表:fused LoRA 会改写
weight.data而 Parameter 对象不变,按 id 索引会取到过期 scalefp8_inference的yield改为try/finally,异常时也能还原F.linearFP8 在 Ascend 上的入口(值得记录,容易踩):
torch.matmul(fp8, fp8)npu_add_quant_matmultransposeX1=true,torch 层固定传 false,eager 路径不可用npu_transpose_quant_batchmatmulperm语义在本版本有 batch 轴推断问题npu_quant_matmul+npu_dynamic_quant(dst_type=292)dst_type是 torch_npu 的内部编号,等于aclDataType + 256:292=ACL_FLOAT8_E4M3FN(36),296=ACL_FLOAT4_E2M1(40),290= HIFLOAT8。scale 的形状/类型有硬性要求:per-channel 权重 scale 必须 1D float32(2D 会被要求转 int64),per-token 激活 scale 必须 float32。
5.
models/qwen_image/qwen_image_dit_fbcache.pyforward补齐缺失参数(edit/context_latents/zero_cond_t/entity_*,txt_seq_lens改名text_seq_lens)——原实现停留在旧 API,开启即TypeErrorwarmup_steps(前 N 步强制计算)与max_skip_streak(限制连续跳过),默认 0 与上游行为一致sequence_parallel上下文内all_reduce(MAX)同步:各卡独立判断会导致「有的卡跳过、有的卡继续」,下一步集合通信时死锁6.
pipelines/qwen_image.pyQwenImageDiTFBCache时补传use_zero_cond_t(原先只有非缓存分支传了,2511 走缓存会算错)7.
tests/common/test_case.pyget_input_image()用with ... as image: return image.convert("RGB")强制加载像素。多设备管线会把输入图 pickle 后传给 worker,PIL 惰性加载会在此时读已关闭的文件句柄并报broken PNG fileassertImageEqual()无论通过与否都打印 SSIM,便于 CI 日志引用(判定与阈值不变)8.
tests/test_pipelines/test_qwen_image_edit_plus.pydevice带设备索引("npu:0")直接报错:mp.spawn的每个 worker 都会先set_device(rank),带索引会让所有 worker 挤到 0 号卡width=1328, height=1328:参考图是 1248×832(由输入图比例自动推算),传入固定尺寸会与参考图尺寸不符,structural_similarity直接抛异常验证
功能正确性
端到端(Qwen-Image-Edit-2509,1248×832,40 步)
量化的取舍:FP8 相对 bf16 掉 0.0026,速度 1.2×,权重显存减半;INT8 掉 0.159——per-token 量程被激活离群值拉爆后小值全被压成 0,而 FP8 的指数位覆盖得住。这也是默认选 fp8 的原因。
已知问题(不在本 PR 范围内)
test_qwen_image_edit_plus.py在 main 上即为 0.659,本 PR 未改变这一行为。tests/data/expect/qwen_image/qwen_image_edit_plus.png生成于 2025-09-30,此后上游有三处改动会改变数值:4215ce4:条件图 resize 由 LANCZOS 改为 PIL 默认(BICUBIC),并给调度器加了shift_terminal=0.020590444:VAE 分块解码相关把这三处临时回退后,单卡可以回到 0.9458——但这属于「复现旧参考图」的工作,不适合塞进「支持 NPU」的 PR。建议单独开 issue 讨论是否更新参考图。
width=1328/height=1328与参考图尺寸(1248×832)不符也源于此,本 PR 只移除了这两个参数,没有改动断言。不包含的内容
torch_npu是可选导入,缺失时IS_NPU为 False)DSNP_BASELINE_ERA、DSNP_SSIM_THRESHOLD等)