[{"content":"你好！我是 Jiawei Qiu，一名专注于 AI infra 的研究与开发者。\n","permalink":"https://javey-q.github.io/about/","summary":"\u003cp\u003e你好！我是 \u003cstrong\u003eJiawei Qiu\u003c/strong\u003e，一名专注于 AI infra 的研究与开发者。\u003c/p\u003e","title":"关于"},{"content":" 系列导读：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。第一篇聚焦于问题背景与性能剖析，第二篇展开全面的单项优化实践，本篇（终篇）进行混合组合优化与吞吐工程部署。\n在前两篇中，我们完成了 SDXL 推理的性能 Profiling 和逐项优化实践。每种单项优化都有其收益上限，而实际工程中往往需要 将多种优化叠加组合 才能逼近性能极限。更进一步，生产部署不仅关注单次延时，更关注 单卡吞吐量（image/s）——这需要从 Batch 策略、多实例部署、GPU 资源调度等系统层面进行优化。\n本篇将回答两个核心问题：\n哪些优化可以叠加？叠加后的实际效果如何？ 如何最大化单卡吞吐？Batch、多实例、MPS 分别适合什么场景？ 实验环境：NVIDIA L20, CUDA 12.x, PyTorch 2.x, 分辨率 1024×1024, 20 步（与生产环境一致）。 Baseline：FP16, 20 步, 单张推理延时 3.9s, 吞吐 0.256 image/s。\n1. 混合优化组合实践 1.1 组合策略设计 并非所有优化都能自由叠加。根据第二篇的分析，各优化作用于 Pipeline 的不同层级：\n组合兼容性矩阵 ═══════════════════════════════════════════════════════════ torch.compile StableFast OneDiff TensorRT VAE FP16 Fix ✅ ✅ ✅ ✅ Tiny VAE ✅ ✅ ✅ ✅ 禁用 CFG ✅ ✅ ✅ ✅ DeepCache ✅ ⚠️ ⚠️ ❌ ⚠️ = 部分兼容，需要额外适配 ❌ = 不兼容或收益冲突 ═══════════════════════════════════════════════════════════ 组合原则：\n编译优化互斥：torch.compile / Stable Fast / OneDiff / TensorRT 只能选其一 组件优化可叠加：VAE 替换、CFG 策略、缓存策略之间互不冲突 编译 + 组件可以叠加：编译优化加速 UNet Kernel，组件优化减少 VAE 开销或去噪次数，作用于不同环节 基于此，我们设计了以下组合实验方案：\n方案 A（精度无损）: 编译优化 + VAE 优化 方案 B（轻微有损）: 编译优化 + VAE 优化 + 禁用 CFG 方案 C（量化有损）: TensorRT + VAE 优化 + INT8 量化 1.2 方案 A：精度无损组合 OneDiff + Tiny VAE from onediff.infer_compiler import oneflow_compile from diffusers import AutoencoderTiny, StableDiffusionXLPipeline vae = AutoencoderTiny.from_pretrained(\u0026#34;madebyollin/taesdxl\u0026#34;, torch_dtype=torch.float16) pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, vae=vae, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34; ).to(\u0026#34;cuda\u0026#34;) pipe.unet = oneflow_compile(pipe.unet) 方法 步长 延时（s） 吞吐（image/s） 显存（GB） 加速比 Base (FP16) 20 3.9 0.256 11.24 1× OneDiff + Tiny VAE 20 3.1 0.322 6.92 1.26× TensorRT + VAE FP16 Fix # TensorRT 引擎构建后，替换 VAE 为 FP16 修复版本 from diffusers import AutoencoderKL vae = AutoencoderKL.from_pretrained(\u0026#34;madebyollin/sdxl-vae-fp16-fix\u0026#34;, torch_dtype=torch.float16) # TensorRT pipeline with custom VAE 方法 步长 延时（s） 吞吐（image/s） 显存 加速比 Base (FP16) 20 3.9 0.256 11.24 GB 1× TensorRT + VAE FP16 Fix 20 2.75 0.363 — 1.41× 分析：TensorRT 组合方案延时降至 2.75s，是无损方案中表现最好的。但需注意 TensorRT 的灵活性限制。\n1.3 方案 B：轻微有损组合 在方案 A 的基础上叠加 部分禁用 CFG，进一步压缩延时。\nStable Fast + Tiny VAE + CFG 方法 步长 延时（s） 吞吐（image/s） 显存（GB） 加速比 Base (FP16) 20 3.9 0.256 11.24 1× StableFast + Tiny VAE + CFG 75% 20 3.0 0.333 7.36 1.30× StableFast + Tiny VAE + CFG 50% 20 2.7 0.370 7.36 1.44× OneDiff + Tiny VAE + CFG 方法 步长 延时（s） 吞吐（image/s） 显存（GB） 加速比 Base (FP16) 20 3.9 0.256 11.24 1× OneDiff + Tiny VAE + CFG 75% 20 2.9 0.344 6.92 1.34× OneDiff + Tiny VAE + CFG 50% 20 2.6 0.384 6.92 1.50× 图片质量对比（OneDiff + Tiny VAE + CFG）：\nBase CFG 75% CFG 50% CFG 75% 方案图片质量与 Baseline 差异极小；CFG 50% 可感知到细节变化，但整体构图一致。\n1.4 方案 C：量化加速组合 TensorRT + Tiny VAE + UNet INT8 TensorRT 支持 INT8 量化推理，通过将 UNet 权重量化为 8 bit 整数，进一步压缩计算量和显存带宽需求。\n量化原理： FP16 权重 (16 bit) → INT8 量化 (8 bit) ├── 计算吞吐量理论翻倍 ├── 显存带宽需求减半 └── 但需要校准数据集（Calibration）确保精度 方法 步长 延时（s） 吞吐（image/s） 加速比 Base (FP16) 20 3.9 0.256 1× TensorRT + Tiny VAE + UNet INT8 20 1.85 0.540 2.1× 图片质量对比：\nBase(FP16) + Tiny VAE + UNet INT8 分析：\n延时降至 1.85s，是目前所有组合方案中 最快的单张推理速度 INT8 量化对图片质量有可感知的影响，尤其是细节纹理和颜色过渡 INT8 量化与 LoRA 的兼容性存在限制：量化后的引擎难以动态加载 LoRA 权重 1.5 混合优化总览 方案 延时（s） 吞吐（image/s） 质量 备注 精度无损 OneDiff + Tiny VAE 3.1 0.322 无损 灵活，适配性好 TensorRT + VAE FP16 Fix 2.75 0.363 无损 无损方案最佳 轻微有损 StableFast + Tiny VAE + CFG 75% 3.0 0.333 细微差别 CFG 稳定性因 prompt 而异 OneDiff + Tiny VAE + CFG 75% 2.9 0.344 细微差别 同上 OneDiff + Tiny VAE + CFG 50% 2.6 0.384 轻微有损 质量风险较高 量化有损 TensorRT + Tiny VAE + INT8 1.85 0.540 有损 LoRA 兼容性差 延时排行（从快到慢）: TRT+TinyVAE+INT8 ███████████░░░░░░░░░ 1.85s (2.1×) OneDiff+Tiny+CFG50 ██████████████░░░░░░ 2.6s (1.5×) TRT+VAE FP16 Fix ██████████████░░░░░░ 2.75s (1.41×) OneDiff+Tiny+CFG75 ███████████████░░░░░ 2.9s (1.34×) SF+Tiny+CFG75 ███████████████░░░░░ 3.0s (1.30×) OneDiff+TinyVAE ████████████████░░░░ 3.1s (1.26×) Baseline (FP16) ████████████████████ 3.9s (1×) 2. 吞吐优化：Batch vs 多实例 vs MPS 单张推理延时的优化存在物理上限。当我们将视角从\u0026quot;单张更快\u0026quot;转向\u0026quot;单位时间出更多图\u0026quot;，就进入了吞吐优化的范畴。本节深入对比三种吞吐提升策略。\n2.1 三种策略的本质差异 策略 1: Batch 推理 ┌─────────────────────────────────┐ │ GPU │ │ ┌───────────────────────────┐ │ │ │ Model (1份) │ │ │ │ Input: [img1, img2, ...] │ │ │ └───────────────────────────┘ │ └─────────────────────────────────┘ → 多个输入拼接为一个大 batch，一次推理 策略 2: 多实例（Multi-Instance） ┌─────────────────────────────────┐ │ GPU │ │ ┌────────────┐ ┌────────────┐ │ │ │ Model A │ │ Model B │ │ │ │ Input: img1│ │ Input: img2│ │ │ └────────────┘ └────────────┘ │ └─────────────────────────────────┘ → 多个独立进程，各自加载模型，独立推理 策略 3: 多实例 + MPS ┌─────────────────────────────────┐ │ GPU (MPS Server) │ │ ┌────────────┐ ┌────────────┐ │ │ │ Model A │ │ Model B │ │ │ │ (50% SM) │ │ (50% SM) │ │ │ └────────────┘ └────────────┘ │ └─────────────────────────────────┘ → 多实例 + MPS 实现真正的 Kernel 并行 维度 Batch 多实例 多实例 + MPS 模型份数 1 份 N 份 N 份 显存开销 低（共享权重） 高（N 倍权重） 高（N 倍权重） 计算量 与 N 张独立推理相同 与 N 张独立推理相同 同左 加速原理 大矩阵 GEMM 效率更高 多进程提高 GPU 利用率 真正并行执行 Kernel 灵活性 低（参数必须一致） 高（各实例独立） 高 适用场景 高并发、统一参数 低并发、异构请求 低并发、异构请求 2.2 Batch 推理实测 Batch 推理的加速原理：GPU 的矩阵运算存在 计算量-速度非线性关系——大矩阵乘法的计算效率远高于多次小矩阵乘法，因为大矩阵能更好地填满 Tensor Core 和利用显存带宽。\nTensorRT + VAE FP16 Fix (Batch=2) 配置 步长 延时（s） 吞吐（image/s） Batch 1 20 2.86 0.350 Batch 2 20 5.55 0.360 OneDiff + Tiny VAE (Batch=2) 配置 步长 延时（s） 吞吐（image/s） Batch 1 20 3.10 0.322 Batch 2 20 5.60 0.357 OneDiff + Tiny VAE + CFG 75% (Batch=2) 配置 步长 延时（s） 吞吐（image/s） Batch 1 20 2.90 0.344 Batch 2 20 5.10 0.392 分析：\nBatch=2 的吞吐提升有限（约 3%~14%），因为 L20 在 Batch=1 时 GPU 利用率已经很高 Batch 推理的 延时翻倍（两张图同时出），对于在线服务来说，单张图的等待时间更长了 Batch 的核心限制：同一 Batch 内所有请求的参数必须完全一致（步长、分辨率、模型权重、LoRA 等） Batch 推理适用场景：高并发且请求参数统一的离线批量生产场景（如电商批量生图）。\n2.3 多实例推理实测 多实例方案通过运行多个独立进程，每个进程加载一份模型，利用 GPU 的时间片轮转来服务不同请求。\n基于 Triton Inference Server（2 实例） Baseline： TensorRT + VAE FP16 Fix， 吞吐：0.350 image/s。 我们使用 Triton Inference Server 部署两个 SDXL 实例，模拟不同到达间隔的请求：\n场景 1：两个请求同时到达（interval=0s）\n延时： 6.25s \u0026amp; 6.25s Throughput: 0.32 image/s ← 反而低于单实例！ 场景 2：两个请求间隔 1s 到达（interval=1s） 延时： 5s \u0026amp; 5s （两个请求间隔 1s 到达） Throughput: 0.40 image/s（虚假吞吐，因为统计窗口内实际有重叠） 场景 3：模拟多个请求间隔 2s 到达（interval=2s） 延时： 5.2s \u0026amp; 6.25s \u0026amp; 6.25s \u0026amp; 5s （4个请求，间隔 2s 到达） Throughput: 0.35 image/s（虚假吞吐，因为统计窗口内实际有重叠） 从这三组结果可以看到一个容易误解的点：多实例并不等价于“更高吞吐”。\n当请求同时到达（高并发、持续排队）时，两个实例会在 GPU 上发生激烈竞争，真实算子执行被串行化，还会引入额外的切换/抖动开销，所以吞吐反而从 0.35 降到 0.32 image/s。 当请求存在间隔（低并发或错峰到达）时，多实例的价值主要体现在降低排队等待（用户感知延时可能更好）。但此时用“短统计窗口”算出来的 Throughput 很容易被请求重叠所“抬高”，出现看似 (0.40) image/s 的虚假吞吐；当模拟多个请求到达情况时，整体吞吐通常仍然接近单实例上限，而不会线性增长。 为什么多实例在高并发下反而更慢？ 这是一个非常关键的发现。根本原因在于 GPU 的时间片轮转调度机制：\nGPU 时间片调度（无 MPS）: 时间 ─────────────────────────────────────────────────────▶ SM: [进程A Kernel] [进程B Kernel] [进程A Kernel] [进程B Kernel] ... ▲ 上下文切换 ▲ 上下文切换 ▲ 上下文切换 问题： 1. 任意时刻只有一个进程占用 GPU → 无真正并行 2. 进程间上下文切换引入额外开销 3. 如果进程 A 未充分利用 SM，空闲资源被浪费（进程 B 无法使用） 在 SDXL 推理场景中，单个模型已经能较好地利用 GPU 资源（利用率 ~98%），多实例在时间片轮转下无法获得额外收益，反而因上下文切换增加了总延时。\n多实例适用场景：非高并发场景下，请求到达存在时间间隔时，后到的请求可通过多实例降低等待延时。但在高并发场景下，多实例反而会降低整体吞吐。\n2.4 CUDA MPS：突破时间片轮转的限制 MPS 原理 NVIDIA MPS（Multi-Process Service） 正是为了解决上述多实例的资源竞争问题而设计的：\n无 MPS（默认时间片轮转）: ┌─────────────────────────────────────────┐ │ GPU │ │ 时间片 1: [进程A 独占所有 SM] │ │ 时间片 2: [进程B 独占所有 SM] │ │ → 交替执行，无真正并行 │ └─────────────────────────────────────────┘ 有 MPS: ┌─────────────────────────────────────────┐ │ GPU (MPS Server 统一管理) │ │ 同一时刻: [进程A 50% SM] [进程B 50% SM] │ │ → 共享 CUDA Context，真正并行 │ └─────────────────────────────────────────┘ MPS 的核心改进：\n真正并行：多个进程的 Kernel 在同一时刻执行，共享 SM 资源 无上下文切换：所有进程通过同一个 CUDA Context 提交任务，消除切换开销 资源隔离：可以通过 CUDA_MPS_ACTIVE_THREAD_PERCENTAGE 控制每个进程可用的计算资源比例 MPS 配置与引擎构建 在第一篇 Profiling 中我们发现：SDXL 推理的瓶颈在于 寄存器和共享内存的限制导致 Occupancy 不足。这意味着单个模型无法充分利用所有 SM 资源，为 MPS 多实例并行创造了条件。\n关键技巧：在 MPS 模式下，需要 限制 TensorRT 引擎构建时的可用计算资源，使引擎在受限条件下搜索最优策略，以期在多实例共享 GPU 时表现更好。\n# Step 1: 设置每个进程可用的计算核心为 50% export CUDA_MPS_ACTIVE_THREAD_PERCENTAGE=50 # Step 2: 在此环境下构建 TensorRT 引擎 # （引擎会在 50% 资源条件下搜索最优 Kernel 配置） python build_trt_engine.py 资源限制效果对比： 简单测试下单实例推理时的吞吐变化：\n限制前： 限制后： 可以发现限制资源后， 单实例吞吐由原先的 0.35 下降到 0.33 image/s，说明资源限制生效了。\nMPS 实测结果 下面测试多实例 + MPS 的吞吐变化：\n# Step 3: 启动 CUDA MPS 控制守护进程 nvidia-cuda-mps-control -d # Step 4: 启动两个推理实例 python inference_instance_1.py \u0026amp; python inference_instance_2.py \u0026amp; 开启MPS（2 实例） 不开启MPS（2 实例） 实际Throughput: 0.38 实际Throughput: 0.32 开启前：两个实例依旧争抢全部 SM，导致 Kernel 执行效率下降 开启MPS后：每个实例可以一定程度上缓解资源竞争，带来资源利用率的提升，吞吐从 0.32 提升到 0.38 image/s，相对无 MPS 双实例提升 18.75%。\n2.5 Batch vs 多实例 vs MPS 决策分析 配置 步长 吞吐（image/s） TensorRT + VAE FP16 Fix 20 0.35 + Batch 2 20 0.36 + 多实例 (×2) 20 0.32 + MPS（2 实例） 20 0.38 综合以上实验结果，三种策略的适用场景如下：\n请求到达模式？ │ ├── 高并发（请求几乎同时到达） │ │ │ ├── 参数一致？ │ │ ├── 是 → Batch 推理（吞吐最高） │ │ └── 否 → MPS 多实例（支持异构请求） │ │ │ └── 注意：纯多实例（无 MPS）在高并发下反而降低吞吐 │ └── 低并发（请求有时间间隔） │ └── 多实例 + MPS ├── 后到请求无需等待前一请求完成 └── 单请求延时不受其他实例影响 3. 最佳工程部署实践 3.1 推荐部署方案 综合所有实验结果，我们给出三套推荐方案：\n方案 1：最高吞吐（轻微有损） OneDiff + Tiny VAE + CFG 75% + Batch 2 ├── 吞吐：0.392 image/s ├── 优点：OneDiff 灵活性好，适配 LoRA；吞吐最高 ├── 缺点：CFG 可能导致部分 prompt 质量波动 └── 适用：高并发、统一参数的批量生产 方案 2：最高吞吐（精度无损 + 异构请求） TensorRT + VAE FP16 Fix + 多实例(×2) + MPS ├── 吞吐：0.37 image/s ├── 优点：精度完全无损；两个实例可独立运行不同请求 ├── 缺点：TensorRT 灵活性差；MPS 存在故障隔离隐患 └── 适用：低并发异构请求、需要精度保证的场景 方案 3：极致单张延时 TensorRT + Tiny VAE + UNet INT8 ├── 单张延时：1.85s ├── 吞吐：0.540 image/s ├── 优点：单张速度最快 ├── 缺点：INT8 量化有精度损失；与 LoRA 兼容性差 └── 适用：对延时极度敏感且可接受质量损失的场景 3.2 部署方案对比总览 方案 延时（s） 吞吐（image/s） 质量 灵活性 风险 Baseline (FP16) 3.9 0.256 基准 最高 — OneDiff+Tiny+CFG75%+Batch2 5.1/张 0.392 轻微有损 高 CFG 不稳定 TRT+VAE FP16+MPS×2 2.86/张 0.370 无损 低 MPS 故障隔离 TRT+Tiny+INT8 1.85/张 0.540 有损 最低 LoRA 不兼容 3.3 生产部署 Checklist 部署前检查清单 ═══════════════════════════════════════════════════════ □ 精度：确认使用 FP16（或 BF16），绝不用 FP32 □ VAE：替换为 madebyollin/sdxl-vae-fp16-fix（零成本） □ 编译：根据场景选择编译框架 □ 固定模型 → TensorRT（提前构建引擎） □ 动态 LoRA → Stable Fast 或 OneDiff □ 吞吐策略： □ 高并发同参数 → Batch 推理 □ 低并发异构 → MPS 多实例 □ MPS 配置（如使用）： □ 启动 MPS 守护进程 □ 设置 CUDA_MPS_ACTIVE_THREAD_PERCENTAGE □ 在受限资源下构建 TensorRT 引擎 □ 监控： □ GPU 利用率 / 显存占用 / 推理延时 □ MPS 故障隔离监控 □ 质量验证： □ 使用业务 prompt 集验证优化后图片质量 □ 特别关注 CFG 优化的稳定性 4. 系列总结 三篇核心脉络回顾 第一篇：知其然 —— 模型剖析与 Profiling ├── SDXL Pipeline 三阶段解构（TextEnc → UNet → VAE） ├── UNet 占 90%+ 推理时间，Attention 访存密集 ├── Nsight Systems 宏观时间线 + Nsight Compute 微观 Kernel 分析 └── 结论：GPU 利用率已高，优化重点在\u0026#34;每个 op 更快\u0026#34;和\u0026#34;减少 op 数量\u0026#34; 第二篇：知其所以然 —— 单项优化实践 ├── 精度优化：FP16 带来 3× 加速（收益最大的单项优化） ├── 编译优化：torch.compile / StableFast / OneDiff / TensorRT (1.1~1.25×) ├── 组件优化：VAE Fix / TinyVAE / CFG / DeepCache / 蒸馏 └── 结论：每种优化都有 trade-off，无银弹 第三篇：学以致用 —— 混合优化与工程部署 ├── 混合组合：TRT+VAE FP16 Fix 无损最佳(1.41×)；TRT+INT8 极速(2.1×) ├── 吞吐三策略：Batch(高并发) / 多实例(低并发) / MPS(真并行) ├── MPS 核心发现：解决多实例资源竞争，吞吐提升 19% └── 三套推荐部署方案 + 生产 Checklist 最终性能达成 指标 优化前 (FP32) 优化后（最佳） 提升倍数 单张延时 16.3s (30步) 1.85s (20步, TRT+INT8) 8.8× 单卡吞吐 ~0.06 image/s 0.392 image/s (OneDiff+Batch) 6.5× 显存占用 18.08 GB 5.76 GB (Batch Process) 3.1× 未来优化方向 DiT 架构：Stable Diffusion 3 / FLUX 等新一代模型采用 DiT（Diffusion Transformer）架构，优化思路将有所不同 Speculative Decoding for Diffusion：借鉴 LLM 的投机解码思想，用小模型预测多步再用大模型验证 动态量化：运行时自适应量化精度，在质量和速度间动态平衡 多 GPU 并行：Tensor Parallel / Pipeline Parallel 在多卡场景下的应用 参考资料：\nUltimate Guide to Optimizing Stable Diffusion XL - Felix Sanz NVIDIA MPS Documentation NVIDIA Triton Inference Server TensorRT Developer Guide DeepCache: Accelerating Diffusion Models for Free (CVPR 2024) OneDiff - GitHub Stable Fast - GitHub SDXL-Lightning - ByteDance ","permalink":"https://javey-q.github.io/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%B8%89%E6%B7%B7%E5%90%88%E4%BC%98%E5%8C%96%E4%B8%8E%E5%90%9E%E5%90%90%E5%B7%A5%E7%A8%8B%E9%83%A8%E7%BD%B2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e系列导读\u003c/strong\u003e：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。\u003ca href=\"/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%B8%80%E6%A8%A1%E5%9E%8B%E5%89%96%E6%9E%90%E4%B8%8E%E6%80%A7%E8%83%BDprofiling/\"\u003e第一篇\u003c/a\u003e聚焦于问题背景与性能剖析，\u003ca href=\"/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%BA%8C%E5%85%A8%E9%9D%A2%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/\"\u003e第二篇\u003c/a\u003e展开全面的单项优化实践，本篇（终篇）进行混合组合优化与吞吐工程部署。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003cp\u003e在前两篇中，我们完成了 SDXL 推理的性能 Profiling 和逐项优化实践。每种单项优化都有其收益上限，而实际工程中往往需要 \u003cstrong\u003e将多种优化叠加组合\u003c/strong\u003e 才能逼近性能极限。更进一步，生产部署不仅关注单次延时，更关注 \u003cstrong\u003e单卡吞吐量\u003c/strong\u003e（image/s）——这需要从 Batch 策略、多实例部署、GPU 资源调度等系统层面进行优化。\u003c/p\u003e\n\u003cp\u003e本篇将回答两个核心问题：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e哪些优化可以叠加？叠加后的实际效果如何？\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e如何最大化单卡吞吐？Batch、多实例、MPS 分别适合什么场景？\u003c/strong\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e实验环境\u003c/strong\u003e：NVIDIA L20, CUDA 12.x, PyTorch 2.x, 分辨率 1024×1024, 20 步（与生产环境一致）。\n\u003cstrong\u003eBaseline\u003c/strong\u003e：FP16, 20 步, 单张推理延时 \u003cstrong\u003e3.9s\u003c/strong\u003e, 吞吐 \u003cstrong\u003e0.256 image/s\u003c/strong\u003e。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-混合优化组合实践\"\u003e1. 混合优化组合实践\u003c/h2\u003e\n\u003ch3 id=\"11-组合策略设计\"\u003e1.1 组合策略设计\u003c/h3\u003e\n\u003cp\u003e并非所有优化都能自由叠加。根据第二篇的分析，各优化作用于 Pipeline 的不同层级：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e组合兼容性矩阵\n═══════════════════════════════════════════════════════════\n               torch.compile  StableFast  OneDiff  TensorRT\nVAE FP16 Fix       ✅            ✅         ✅       ✅\nTiny VAE           ✅            ✅         ✅       ✅\n禁用 CFG            ✅            ✅         ✅       ✅\nDeepCache          ✅            ⚠️         ⚠️       ❌\n\n⚠️ = 部分兼容，需要额外适配\n❌ = 不兼容或收益冲突\n═══════════════════════════════════════════════════════════\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e组合原则：\u003c/p\u003e","title":"SDXL 推理极限优化实战（三）：混合优化与吞吐工程部署"},{"content":" 系列导读：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。第一篇聚焦于问题背景与性能剖析，本篇展开全面的单项优化实践，第三篇进行混合组合优化与吞吐工程部署。\n在上一篇中，我们通过 Profiling 明确了 SDXL 推理的性能瓶颈：UNet 去噪循环占据 90%+ 的推理时间，Attention 层受限于访存带宽，大量小 Kernel 引入启动开销，CFG 使计算量翻倍。本篇将基于这些发现，从三个维度逐一展开优化实践：\n优化维度总览 ══════════════════════════════════════════════════════ 精度 维度 1 数值精度优化 无损 ──────▶ 有损 FP32 → FP16 / BF16 / TF32 ████░░░░░░ 维度 2 编译与算子层优化 无损 torch.compile / StableFast ██████████ OneDiff / TensorRT 维度 3 模型组件级优化 无损 ──────▶ 有损 VAE Fix / TinyVAE / CFG / ████████░░ DeepCache / 蒸馏 / 显存优化 ══════════════════════════════════════════════════════ Baseline 约定：所有实验以 FP16、30 步、1024×1024 为基准（延时 5.5s，显存 11.24GB），在 NVIDIA L20 上执行。\n1. 数值精度优化 数值精度是推理优化中 投入最小、收益最大 的一环。它的本质是用更少的 bit 来表示数字，从而同时减少计算量和访存带宽需求。\n1.1 浮点格式速览 在深入实验之前，先理解四种常用浮点格式的区别：\n符号 指数 尾数 总位数 数值范围 精度 FP32: 1 8 23 32 ±3.4×10³⁸ ~7 位有效数字 TF32: 1 8 10 19* ±3.4×10³⁸ ~3 位有效数字 FP16: 1 5 10 16 ±65504 ~3 位有效数字 BF16: 1 8 7 16 ±3.4×10³⁸ ~2 位有效数字 * TF32 仅在 Tensor Core 内部使用，存储仍为 32 bit 关键差异：\nFP16 vs BF16：FP16 精度更高（10 bit 尾数），但数值范围小（易溢出）；BF16 范围与 FP32 一致（8 bit 指数），但精度较低 TF32：NVIDIA Ampere+ 架构的 Tensor Core 特有格式，存储为 FP32（不省显存），计算用 TF32（略微加速） 1.2 FP32 → FP16：最基本的优化 FP16 优化的核心原理：\n计算加速：FP16 Tensor Core 吞吐量是 FP32 的 2~4 倍（取决于架构） 带宽减半：每个数据从 4 Byte 变为 2 Byte，对访存密集型算子（如 Attention）收益显著 显存减半：模型权重和中间激活值的显存占用大幅降低 from diffusers import StableDiffusionXLPipeline import torch # FP32（默认） pipe_fp32 = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float32 ).to(\u0026#34;cuda\u0026#34;) # FP16 pipe_fp16 = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34; ).to(\u0026#34;cuda\u0026#34;) 实测结果：\n精度 步长 延时（s） 速度（step/s） 显存（GB） 加速比 FP32 30 16.3 1.84 18.08 1× FP16 30 5.5 5.45 11.24 2.96× FP32 50 26.9 1.85 18.07 1× FP16 50 8.7 5.74 11.24 3.09× 图片质量对比：\nFP32 FP16 FP16 与 FP32 的生成质量几乎无差异，肉眼不可区分。FP16 精度对于扩散模型的去噪过程完全足够。\n1.3 TF32：看似折中，实则鸡肋 TF32（TensorFloat-32）是 NVIDIA 在 Ampere 架构中引入的计算格式，设计初衷是\u0026quot;无需改代码即可获得加速\u0026quot;。它使用与 FP32 相同的指数位（保持数值范围），但将尾数截断到 10 bit（与 FP16 相同）。\n# 启用 TF32（PyTorch 2.x 中默认已启用） torch.backends.cuda.matmul.allow_tf32 = True torch.backends.cudnn.allow_tf32 = True 实测结果：\n精度 步长 延时（s） 速度（step/s） 显存（GB） 加速比 FP32 30 16.3 1.84 18.08 1× TF32 30 12.4 2.41 18.08 1.31× 为什么 TF32 加速不明显？\nTF32 的局限性在于：\n不省显存：数据存储仍为 32 bit，显存带宽没有减少 仅加速 Tensor Core 运算：只有 GEMM 和卷积运算受益，Element-wise 操作不受影响 在 L20 上收益有限：L20 的 FP32 性能已经很强，TF32 的加速幅度有限 结论：TF32 加速仅 1.31×，远不如 FP16 的 2.96×，且不节省显存。不建议在 SDXL 推理中使用。\n1.4 BF16：FP16 的强力替代 BF16（Brain Floating Point 16）与 FP16 的关键区别在于用更多的指数位换取更大的数值范围，牺牲了部分精度。\npipe_bf16 = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.bfloat16 ).to(\u0026#34;cuda\u0026#34;) 实测结果：\n精度 步长 延时（s） 速度（step/s） 显存（GB） 加速比 FP32 30 16.3 1.84 18.08 1× BF16 30 5.4 5.55 9.62 3.01× BF16 相比 FP16 的优势：\n速度基本持平（5.4s vs 5.5s） 显存更低（9.62 GB vs 11.24 GB，降低 14%） 数值范围更大，训练稳定性更好（但推理场景差异不大） 1.5 精度优化总结 精度 延时（s） 显存（GB） 加速比 质量 推荐 FP32 16.3 18.08 1× 基准 仅调试用 TF32 12.4 18.08 1.31× 无损 不推荐 FP16 5.5 11.24 2.96× 无损 默认首选 BF16 5.4 9.62 3.01× 无损 显存紧张时选用 实践建议：FP16 是 SDXL 推理的默认精度选择，兼顾通用性和性能。BF16 在显存受限时是更优解。后续所有优化均基于 FP16 展开。\n2. 编译与算子层优化 精度优化解决了\u0026quot;用更少的 bit 做同样的计算\u0026quot;，编译优化则解决\u0026quot;用更少的 Kernel 做同样的计算\u0026quot;。这类优化工作在底层，对模型精度完全无损，但通常需要一次性的编译开销。\n2.1 为什么需要编译优化？ 在上一篇的 Profiling 中，我们观察到 SDXL 在 PyTorch eager 模式下存在两个关键问题：\nKernel 数量爆炸：UNet 单步前向传播触发 847 个 CUDA Kernel，每个 Kernel 启动有 510μs 的固定开销 算子碎片化：Conv → BN → ReLU 等模式被拆成独立 Kernel，中间结果要反复 store/load 到显存 编译优化的核心手段：\n优化前（Eager Mode）: Conv Kernel → 写回显存 → 读取 → BN Kernel → 写回 → 读取 → ReLU Kernel (3 次 Kernel Launch, 4 次显存 Read/Write) 优化后（算子融合）: Fused_Conv_BN_ReLU Kernel (1 次 Kernel Launch, 1 次显存 Read/Write) 2.2 torch.compile：PyTorch 原生方案 torch.compile 是 PyTorch 2.0 引入的编译 API，通过 TorchDynamo 捕获计算图，再交由后端（TorchInductor）进行优化，包括算子融合和 CUDA Graphs。\n基础模式 pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float16 ).to(\u0026#34;cuda\u0026#34;) # 编译 UNet 和 VAE pipe.unet = torch.compile(pipe.unet, mode=\u0026#34;reduce-overhead\u0026#34;, fullgraph=True) pipe.vae.decode = torch.compile(pipe.vae.decode, mode=\u0026#34;reduce-overhead\u0026#34;, fullgraph=True) 编译耗时：约 1 分钟。\n方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× torch.compile (reduce-overhead) 30 5.2 5.77 11.24 1.05× max-autotune 模式 max-autotune 模式会启用 CUDA Graphs 并尝试更多的 Kernel 配置来寻找最优方案：\npipe.unet = torch.compile(pipe.unet, mode=\u0026#34;max-autotune\u0026#34;, fullgraph=True) pipe.vae.decode = torch.compile(pipe.vae.decode, mode=\u0026#34;max-autotune\u0026#34;, fullgraph=True) 编译耗时：较长且不稳定（5~16 分钟）。\n方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× torch.compile (max-autotune) 30 4.9 5.77 11.24 1.12× mode 参数说明 Mode 说明 编译耗时 推理加速 default 基础优化，平衡编译与运行时 短 小 reduce-overhead 减少 Python 开销和 Kernel Launch 开销 中 中 max-autotune 启用 CUDA Graphs + 自动调优，针对 latency 优化 长 最大 max-autotune-no-cudagraphs 自动调优但不使用 CUDA Graphs 中 中 CUDA Graphs 原理：将一系列 CUDA 操作录制为一个 Graph，后续执行时通过 单次 CPU 调用 触发整个 Graph，消除逐 Kernel 启动的开销。特别适合 SDXL 这种 \u0026ldquo;同一计算重复执行多步\u0026rdquo; 的场景。\n2.3 Stable Fast：超轻量编译框架 stable-fast 是专为 HuggingFace Diffusers 优化的超轻量推理框架，采用了多项底层优化技术：\ncuDNN 卷积融合：完整支持 Conv + Bias + Add + Act 计算模式的融合 低精度融合 GEMM：使用 FP16 精度进行计算，比 PyTorch 默认（读写 FP16，计算 FP32）更快 NHWC 融合 GroupNorm：通过 OpenAI Triton 实现高效的 GroupNorm + GELU 融合，消除 channels-last 下不必要的内存排列 全模型 Trace：改进 torch.jit.trace 适配复杂模型，支持 ControlNet 和 LoRA CUDA Graph：将 UNet 捕获为 CUDA Graph，减少小 batch 下的 CPU 开销 融合多头自注意力：集成 xformers 并使其与 TorchScript 兼容 from sfast.compilers.diffusion_pipeline_compiler import compile, CompilationConfig pipe = AutoPipelineForText2Image.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, use_safetensors=True, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34;, ).to(\u0026#34;cuda\u0026#34;) config = CompilationConfig.Default() config.enable_xformers = True config.enable_triton = True config.enable_cuda_graph = True pipe = compile(pipe, config) 首次编译耗时：仅 18 秒（这是 Stable Fast 最大的优势）。\n方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× Stable Fast 30 5.0 6.0 12.12 1.10× Base (FP16) 50 8.7 5.74 11.24 1× Stable Fast 50 7.9 6.32 12.12 1.10× 分析：速度提升 10%，显存略微增加（CUDA Graph 预分配的额外开销）。质量完全无损。\nStable Fast 的核心优势不在于绝对加速比，而在于 10 秒级的编译速度。相比 TensorRT 需要 20 分钟的引擎构建，Stable Fast 在需要频繁切换模型（如 LoRA 热更新）的场景中有不可替代的优势。\n2.4 OneDiff：一行代码加速 OneDiff 由硅基流动（SiliconFlow）开源，通过改进的注意力机制和深度图编译优化来加速扩散模型。\nfrom onediff.infer_compiler import oneflow_compile pipe = AutoPipelineForText2Image.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, use_safetensors=True, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34;, ).to(\u0026#34;cuda\u0026#34;) pipe.unet = oneflow_compile(pipe.unet) 首次编译耗时：约 55 秒。\n方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× OneDiff 30 4.8 6.25 7.24 1.14× Base (FP16) 50 8.7 5.74 11.24 1× OneDiff 50 7.5 6.66 7.24 1.16× 分析：OneDiff 不仅加速了 14~16%，还显著降低了显存占用（11.24 → 7.24 GB，降低 36%）。这得益于 OneFlow 后端更高效的内存管理和计算图优化。\n2.5 TensorRT：NVIDIA 官方高性能推理引擎 TensorRT 是 NVIDIA 打造的专用推理优化器，拥有最深度的硬件级优化能力：\n优化方法：\n算子融合（Layer \u0026amp; Tensor Fusion）：将多个计算 op 融合，减少数据搬运和显存访问 Kernel 自动调优（Auto-Tuning）：根据具体 GPU 架构（SM 数量、频率等），在候选 Kernel 中搜索最优实现 多流执行（Multi-Stream）：利用 CUDA Stream 最大化并行执行 CUDA Graphs：减少 Kernel Launch 开销 量化支持：支持 INT8 / FP16 等低精度推理 # TensorRT 集成方式（通过 diffusers 的 TensorRT 后端） # 需要先将模型转为 ONNX，再构建 TensorRT Engine # 详细步骤参见 NVIDIA TensorRT 文档 # 编译流程：Model → ONNX → TensorRT Engine # 编译耗时：约 20 分钟（包括 ONNX 导出和 Engine 构建） 方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× TensorRT 30 4.4 6.12 11.24 1.25× Base (FP16) 50 8.7 5.74 11.24 1× TensorRT 50 7.2 6.58 11.24 1.20× TensorRT 取得了编译优化中最高的加速比（1.25×），但也存在明显限制：\n优势 限制 加速比最高 编译耗时长（~20min） NVIDIA 官方维护，稳定可靠 不支持动态 shape（需预设输入尺寸） 持续更新针对新架构的优化 使用不灵活，需提前构建 Engine 支持 INT8 量化进一步加速 LoRA 切换需重新编译 2.6 编译优化对比总结 方案 加速比 显存 编译耗时 灵活性 适用场景 torch.compile 1.12× 11.24 GB 5~16 min 高 通用场景 Stable Fast 1.10× 12.12 GB 18s 高 频繁切换模型 / LoRA OneDiff 1.16× 7.24 GB 55s 中 显存敏感场景 TensorRT 1.25× 11.24 GB ~20 min 低 固定模型长期部署 选型建议：\n追求最大加速且模型固定 → TensorRT 需要频繁切换 LoRA / 模型 → Stable Fast（编译最快） 显存紧张 → OneDiff（显存占用最低） 不想引入额外依赖 → torch.compile（PyTorch 原生） 重要发现：在 L20 上，编译优化的加速比（1.10~1.25×）普遍不及官方宣称的数据。这可能是因为 L20 本身的 FP16 计算能力已经很强，Eager 模式下 GPU 利用率已达 ~98%，留给编译优化的空间有限。在算力更弱的消费级 GPU 上，编译优化的收益可能更显著。\n3. 模型组件级优化 与编译优化作用于底层 Kernel 不同，组件级优化直接修改 SDXL Pipeline 的各个组成部分。这些优化有的完全无损，有的会轻微影响图片质量，但它们可以 叠加使用，累积效果可观。\n3.1 VAE FP16 Fix（无损） 问题背景 SDXL 默认的 VAE 在 FP16 下会产生 NaN（数值溢出），因此官方代码在解码前强制将 VAE upcast 到 FP32：\n# diffusers 内部逻辑 pipe.upcast_vae() # 强制 VAE 以 FP32 运行 这意味着即使整体 Pipeline 使用 FP16，VAE 解码阶段仍然在浪费 FP32 的计算和显存。\n解决方案 社区开发者 madebyollin 创建了修补版 VAE，使其能够在 FP16 下稳定运行：\nfrom diffusers import AutoencoderKL # 加载 FP16 兼容的 VAE vae = AutoencoderKL.from_pretrained( \u0026#34;madebyollin/sdxl-vae-fp16-fix\u0026#34;, torch_dtype=torch.float16 ) pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, vae=vae, torch_dtype=torch.float16 ).to(\u0026#34;cuda\u0026#34;) 方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× VAE FP16 Fix 30 5.4 5.55 9.62 1.01× 图片质量对比：\nBase VAE FP16 分析：延时几乎不变（VAE 本身占比小），但显存从 11.24 GB 降至 9.62 GB（节省 14%）。图片质量完全无损。\n结论：VAE FP16 Fix 是零成本优化，建议始终启用。\n3.2 Tiny VAE（基本无损） 原理 SDXL 默认 VAE 拥有约 5000 万 参数。TAESD（Tiny AutoEncoder for Stable Diffusion）是原始 VAE 的精简版本，仅 100 万 参数（缩减 50 倍），且原生支持 FP16。\nfrom diffusers import AutoencoderTiny # 使用 Tiny VAE vae = AutoencoderTiny.from_pretrained( \u0026#34;madebyollin/taesdxl\u0026#34;, torch_dtype=torch.float16 ) pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, vae=vae, torch_dtype=torch.float16 ).to(\u0026#34;cuda\u0026#34;) 方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× Tiny VAE 30 5.1 5.88 7.57 1.08× 图片质量对比：\nBase Tiny VAE 分析：\n延时减少 0.4s（VAE 解码加速显著） 显存降低 33%（11.24 → 7.57 GB） 图片质量基本无损，极少数情况可能出现细微的纹理差异 结论：Tiny VAE 以微小的质量代价换取了可观的显存节省和轻微的速度提升，推荐在显存敏感场景使用。\n3.3 禁用 CFG（轻微有损） 原理 Classifier-Free Guidance（CFG）是扩散模型中控制生成质量的核心技术。它通过同时预测 有条件 和 无条件 两个噪声，然后按比例混合：\nnoise_pred = noise_uncond + guidance_scale × (noise_cond - noise_uncond) 这意味着每个去噪步骤中，UNet 需要执行 两次 前向传播（通过将 conditional 和 unconditional 拼接为 batch=2 实现），计算量直接翻倍。\n核心思考：CFG 在早期步骤中至关重要（引导模型走向正确的语义方向），但在后期步骤中，模型已经\u0026quot;走上正轨\u0026quot;，是否还需要 CFG？\n实验：在后续步骤中关闭 CFG # 在 75% 的步骤后关闭 CFG image = pipe( prompt, num_inference_steps=30, guidance_scale=7.5, # 自定义回调：在第 22 步后将 guidance_scale 设为 1（即关闭 CFG） callback_on_step_end=lambda pipe, step, ts, kwargs: {**kwargs, \u0026#34;guidance_scale\u0026#34;: 1.0} if step \u0026gt; 22 else kwargs ) 方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× CFG 75%（后 25% 关闭） 30 5.1 5.88 11.24 1.08× CFG 50%（后 50% 关闭） 30 4.6 6.52 11.24 1.19× 图片质量对比：\nBase CFG 75% CFG 50% 分析：\nCFG 75%：图片与 Baseline 差异极小，大多数场景可接受 CFG 50%：开始出现可感知的细节差异，但整体构图和语义不变 结论：CFG 75% 是一个不错的折中点（加速 8%，质量几乎无损）。CFG 50% 加速更大（19%）但需要接受一定的质量波动。注意：该优化对不同 prompt 的稳定性不一致，部分场景可能导致质量显著下降，建议配合业务验证使用。\n3.4 DeepCache（有损，高加速比） 原理 DeepCache（CVPR 2024）的核心思想是：在 UNet 的去噪循环中，相邻步骤的高层特征变化很小。因此可以每隔若干步才真正执行一次完整的 UNet 前向传播，中间步骤复用（缓存）之前的高层特征。\n正常推理（30步）: UNet UNet UNet UNet UNet ... (30 次完整计算) DeepCache(间隔2): UNet Cache UNet Cache UNet ... (15 次完整 + 15 次缓存) DeepCache(间隔3): UNet Cache Cache UNet Cache Cache ... (10 次完整 + 20 次缓存) 两个关键参数：\ncache_interval：缓存更新频率，值越大加速越多但质量越低 cache_branch_id：指定缓存 UNet 的哪个层级（0 = 最深层，值越大 = 越浅层） from DeepCache import DeepCacheSDHelper helper = DeepCacheSDHelper(pipe=pipe) helper.set_params(cache_interval=3, cache_branch_id=0) helper.enable() image = pipe(prompt, num_inference_steps=30).images[0] 方法 cache_interval cache_branch_id 延时（s） 速度（step/s） 加速比 Base (FP16) - - 5.5 5.45 1× DeepCache 2 1 3.3 9.09 1.66× DeepCache 2 0 3.2 9.37 1.72× DeepCache 3 1 2.5 12.0 2.20× DeepCache 3 0 2.4 12.5 2.29× 图片质量对比：\nBase cache_interval=2 cache_branch_id=1 cache_interval=2 cache_branch_id=0 cache_interval=3 cache_branch_id=1 cache_interval=3 cache_branch_id=0 分析：\ncache_interval=2 时加速约 1.7×，图片整体布局不变，细节有轻微差异 cache_interval=3 时加速高达 2.29×，但图片细节变化更明显 图片的整体构图和语义一致性保持良好，主要差异在纹理细节 结论：DeepCache 提供了所有组件优化中 最高的加速比。虽然有质量损失，但布局不变，非常适合 Prompt 调试和快速预览 场景。对于最终出图建议关闭或使用保守参数（cache_interval=2）。\n3.5 蒸馏模型：SDXL-Lightning（有损，极高加速比） 原理 SDXL-Lightning 是字节跳动推出的蒸馏模型，采用 渐进式对抗蒸馏（Progressive Adversarial Distillation） 技术，使模型能在极少步数（2~8 步）内生成高质量图像。\n这不是对原始模型的加速，而是用一个全新的蒸馏模型 替代 原始模型。\nfrom diffusers import StableDiffusionXLPipeline, EulerDiscreteScheduler from huggingface_hub import hf_hub_download # 加载 SDXL-Lightning 的 LoRA 权重 pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34; ).to(\u0026#34;cuda\u0026#34;) # 使用 4-step 版本 pipe.load_lora_weights(hf_hub_download( \u0026#34;ByteDance/SDXL-Lightning\u0026#34;, \u0026#34;sdxl_lightning_4step_lora.safetensors\u0026#34; )) pipe.fuse_lora() pipe.scheduler = EulerDiscreteScheduler.from_config( pipe.scheduler.config, timestep_spacing=\u0026#34;trailing\u0026#34; ) image = pipe(prompt, num_inference_steps=4, guidance_scale=0).images[0] 方法 步长 延时（s） 显存（GB） 加速比 Base (FP16) 30 5.5 11.24 1× SDXL-Lightning 8 1.5 11.25 3.67× SDXL-Lightning 4 1.1 11.25 5× SDXL-Lightning 2 1.0 11.25 5.5× 图片质量对比：\nBase Step 2 Step 4 Step 8 分析：\n加速比极高（4 步版本可达 5×） 但生成风格与原始 SDXL 存在明显差异，倾向于更\u0026quot;插画风\u0026quot; 2 步版本质量明显下降，4 步和 8 步版本质量较为可接受 结论：蒸馏模型提供了极致的速度，但以 风格一致性 为代价。适用于对风格要求宽松、追求极速的场景（如实时预览、批量草图生成）。\n3.6 显存优化（牺牲速度换显存） 以下两种优化专注于降低显存占用，适用于显存紧张的场景。它们 会增加推理延时，仅在硬件条件受限时使用。\n模型 CPU 卸载 在推理过程中，按需将当前阶段的模型移入 GPU，其余模型暂存于 CPU 内存：\n# 加载顺序：text_encoder → text_encoder_2 → image_encoder → unet → vae pipe.enable_model_cpu_offload() 方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× CPU Offload 30 6.7 4.48 7.05 0.82× 延时增加 22%，显存降低 37%。模型搬运（CPU ↔ GPU）引入了额外的数据传输开销。\n批处理加载 对一个 batch 的数据，顺序加载 Pipeline 中的各个组件并执行推理，每完成一个组件就将其卸载。本质上是 CPU Offload 的 batch 化变体，用批次处理来掩盖模型加载延时。\n方法 步长 延时（s） 速度（step/s） 显存（GB） 加速比 Base (FP16) 30 5.5 5.45 11.24 1× Batch Process (B=4) 30 6.0 5.0 5.76 0.91× 注：此处延时为 Batch=4 时的平均延时。\n结论：显存优化方案以牺牲速度为代价。在 L20（48GB）上通常不需要使用。仅当部署在消费级 GPU（如 RTX 3060 12GB）时才考虑。\n3.7 质量优化（不加速，提升生成效果） 以下两种方法专注于提升图片质量，不影响推理速度，作为可选项供参考。\nFreeU：平衡 U-Net 特征 FreeU 通过调整 UNet 跳跃连接（Skip Connection）和主干特征图的贡献权重，抑制不自然的高频细节，使生成结果更真实。\n# 启用 FreeU pipe.enable_freeu(s1=0.9, s2=0.2, b1=1.5, b2=1.6) 方法 步长 延时（s） 显存（GB） Base (FP16) 30 5.5 11.24 FreeU 30 5.5 11.24 速度和显存完全不变，仅影响生成风格。\nBase FreeU Refiner：专用细化模型 SDXL 提供了一个专用的 Refiner 模型，通过 Ensemble of Expert Denoisers 方式使用：Base 模型执行前面若干步，Refiner 模型执行后面若干步。\nfrom diffusers import StableDiffusionXLPipeline, StableDiffusionXLImg2ImgPipeline base = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float16 ).to(\u0026#34;cuda\u0026#34;) refiner = StableDiffusionXLImg2ImgPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-refiner-1.0\u0026#34;, torch_dtype=torch.float16 ).to(\u0026#34;cuda\u0026#34;) # Base 20步 + Refiner 10步 image = base(prompt, num_inference_steps=20, denoising_end=0.8, output_type=\u0026#34;latent\u0026#34;).images image = refiner(prompt, image=image, num_inference_steps=10, denoising_start=0.8).images[0] 方法 步长 延时（s） 显存（GB） Base (FP16) 30 5.5 11.24 Base + Refiner 20+10 5.6 18.3 Base Refiner 分析：延时基本不变，但显存大幅增加（需要同时加载两个大模型）。图片在细节层面有提升。\n4. 组件优化全景对比 将所有单项优化汇总为一张对比表：\n类别 方法 加速比 显存（GB） 图片质量 使用建议 备注 精度 FP16 — 11.24 无损 始终使用 作为 Baseline 编译 torch.compile 1.12× 11.24 无损 可以使用 编译耗时不稳定 Stable Fast 1.10× 12.12 无损 可以使用 编译最快（18s） OneDiff 1.16× 7.24 无损 可以使用 显存最低 TensorRT 1.25× 11.24 无损 考虑使用 不灵活，需预构建引擎 组件 VAE FP16 Fix 1.01× 9.62 无损 始终使用 零成本优化 Tiny VAE 1.08× 7.57 基本无损 推荐使用 禁用 CFG 75% 1.08× 11.24 细微差别 考虑使用 稳定性因 prompt 而异 DeepCache (i=3) 2.29× 11.59 有损 可选 适合 Prompt 调试 蒸馏 (Lightning) 5× 11.25 风格变化 可选 适合快速预览 显存 CPU Offload 0.82× 7.05 无损 不推荐 显存不够时考虑 Batch Process 0.91× 5.76 无损 不推荐 显存不够时考虑 质量 FreeU 1× 11.24 风格变化 可选 不影响速度 Refiner 0.98× 18.3 细节增强 可选 需额外显存 5. 小结与展望 本篇核心收获 精度优化是收益最大的单项优化：FP32 → FP16 带来 ~3× 加速，是后续所有优化的基础 编译优化无损但有限：在 L20 上加速 1.10~1.25×，不同框架各有侧重（速度 vs 灵活性 vs 显存） 组件优化可叠加：VAE FP16 Fix + Tiny VAE + 部分禁用 CFG 的组合可以在几乎不损失质量的前提下获得可观加速 DeepCache 和蒸馏是\u0026quot;空间换质量\u0026quot;的极端方案：加速比极高但有明显的质量代价 没有银弹：每种优化都有其 trade-off，需要根据业务场景（质量要求、延时预算、显存限制）做取舍 关键 Trade-off 决策树 你的场景是什么？ │ ├── 追求最大质量，延时可接受 │ └── FP16 + VAE FP16 Fix + (可选) Refiner │ ├── 追求低延时，质量基本无损 │ └── FP16 + TensorRT/OneDiff + Tiny VAE + VAE FP16 Fix │ ├── 追求极致延时，接受轻微质量损失 │ └── FP16 + OneDiff + Tiny VAE + CFG 75% │ ├── Prompt 调试 / 快速预览 │ └── FP16 + DeepCache (interval=3) │ └── 实时交互 / 极速生成 └── SDXL-Lightning (4-step) 下篇预告 在第三篇（终篇）中，我们将：\n混合组合优化：将编译优化与组件优化叠加，测试 TensorRT + Tiny VAE、OneDiff + Tiny VAE + CFG 等组合的实际效果 吞吐优化实战：深入对比 Batch 推理、多实例部署、CUDA MPS 三种吞吐提升策略 最佳工程部署实践：给出不同并发场景下的最优部署方案和配置建议 敬请期待。\n参考资料：\nUltimate Guide to Optimizing Stable Diffusion XL - Felix Sanz stable-fast - GitHub OneDiff - GitHub DeepCache: Accelerating Diffusion Models for Free (CVPR 2024) SDXL-Lightning - ByteDance NVIDIA TensorRT Documentation HuggingFace Diffusers - Optimization Guide ","permalink":"https://javey-q.github.io/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%BA%8C%E5%85%A8%E9%9D%A2%E4%BC%98%E5%8C%96%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e系列导读\u003c/strong\u003e：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。\u003ca href=\"/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%B8%80%E6%A8%A1%E5%9E%8B%E5%89%96%E6%9E%90%E4%B8%8E%E6%80%A7%E8%83%BDprofiling/\"\u003e第一篇\u003c/a\u003e聚焦于问题背景与性能剖析，本篇展开全面的单项优化实践，第三篇进行混合组合优化与吞吐工程部署。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003cp\u003e在上一篇中，我们通过 Profiling 明确了 SDXL 推理的性能瓶颈：UNet 去噪循环占据 90%+ 的推理时间，Attention 层受限于访存带宽，大量小 Kernel 引入启动开销，CFG 使计算量翻倍。本篇将基于这些发现，\u003cstrong\u003e从三个维度逐一展开优化实践\u003c/strong\u003e：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e优化维度总览\n══════════════════════════════════════════════════════\n                                        精度\n维度 1  数值精度优化                     无损 ──────▶ 有损\n        FP32 → FP16 / BF16 / TF32      ████░░░░░░\n\n维度 2  编译与算子层优化                 无损\n        torch.compile / StableFast      ██████████\n        OneDiff / TensorRT\n\n维度 3  模型组件级优化                   无损 ──────▶ 有损\n        VAE Fix / TinyVAE / CFG /       ████████░░\n        DeepCache / 蒸馏 / 显存优化\n══════════════════════════════════════════════════════\n\u003c/code\u003e\u003c/pre\u003e\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eBaseline 约定\u003c/strong\u003e：所有实验以 \u003cstrong\u003eFP16、30 步、1024×1024\u003c/strong\u003e 为基准（延时 5.5s，显存 11.24GB），在 NVIDIA L20 上执行。\u003c/p\u003e","title":"SDXL 推理极限优化实战（二）：全面优化实践指南"},{"content":" 系列导读：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。第一篇聚焦于问题背景与性能剖析，第二篇展开全面的优化实践，第三篇进行混合组合优化与吞吐工程部署。\n1. 为什么要优化 SDXL 推理？ 1.1 SDXL 的工程地位 Stable Diffusion XL（SDXL）是 Stability AI 于 2023 年发布的旗舰级文生图模型，相较于 SD 1.5，它在图像质量、分辨率（原生支持 1024×1024）和语义理解能力上有了质的飞跃。SDXL 已成为开源社区中应用最广泛的高分辨率生成模型之一，广泛用于创意设计、电商素材生成、游戏概念图制作等场景。\n然而，更强的模型能力伴随着更高的计算开销。SDXL 的参数量达到约 3.5B（Base UNet 2.6B + Text Encoders 等），相较于 SD 1.5 的 ~0.9B，推理成本大幅增加：\n指标 SD 1.5 SDXL UNet 参数量 ~860M ~2.6B 原生分辨率 512×512 1024×1024 Text Encoder CLIP ViT-L/14 CLIP ViT-L/14 + OpenCLIP ViT-bigG FP32 显存占用 ~4 GB ~18 GB FP16 单次推理延时（30步） ~1.5s ~5.5s 1.2 优化的业务价值 在生产环境中，推理性能直接决定了：\n用户体验：单张图片从 5.5s 降至 2.8s，交互延迟感知有本质差异 服务成本：GPU 是最昂贵的资源，推理加速 = 同等算力下服务更多用户 吞吐能力：高并发场景下，从 0.25 image/s 提升到 0.37+ image/s 意味着单卡产能提升近 50% 1.3 实验环境 本系列所有实验基于以下硬件与软件环境：\n硬件：NVIDIA L20\n规格 参数 GPU 架构 NVIDIA Ada Lovelace CUDA 核心 10240 频率 高达 2.2 GHz 显存 48GB GDDR6 显存位宽 384 位 显存带宽 864 GB/s FP32 算力 90 TFLOPS FP16 Tensor Core 算力 ~181 TFLOPS 软件环境：\nCUDA 12.x PyTorch 2.x Diffusers (HuggingFace) NVIDIA Nsight Systems / Nsight Compute 1.4 优化评估三要素 在评估推理优化效果时，我们关注三个核心指标：\n速度（Latency）：单次推理端到端延时，直接影响用户体验 资源占用（Memory）：主要关注显存占用，决定了并行度上限和可部署的 Batch Size 图片质量（Quality）：优化不能以牺牲可感知的图片质量为代价，需要在速度和质量间取得平衡 2. SDXL 推理 Pipeline 深度剖析 2.1 端到端推理流程 SDXL 的推理是一个多模型协作的 Pipeline，而非单一模型的前向传播。理解这一点是性能优化的前提。\n┌─────────────┐ ┌─────────────────────────────────────┐ ┌───────────┐ │ Text Encode │───▶│ UNet Denoising Loop │───▶│ VAE Decode│ │ (CLIP×2) │ │ (Iterative: 20~50 steps) │ │ │ └─────────────┘ └─────────────────────────────────────┘ └───────────┘ ~0.1s ~5.0s (主要瓶颈) ~0.3s 完整的推理流程如下：\nStep 1：文本编码（Text Encoding）\nSDXL 使用双 Text Encoder 架构：\nCLIP ViT-L/14：输出 768 维文本嵌入 OpenCLIP ViT-bigG/14：输出 1280 维文本嵌入 两个编码器的输出被拼接为 2048 维向量，作为 UNet 的条件输入。此阶段通常只执行一次，耗时占比极小（\u0026lt;2%）。\nStep 2：UNet 去噪循环（Denoising Loop）—— 计算核心\nUNet 是整个 Pipeline 的计算瓶颈，占据了 90%+ 的推理时间。在每个去噪步骤中，UNet 接收当前的 noisy latent 和文本条件，预测噪声残差。\n关键参数：\n输入 latent shape：(B, 4, 128, 128) — 对应 1024×1024 图像 模型参数量：~2.6B 每步包含大量 Self-Attention、Cross-Attention 和 Conv 运算 如果启用了 CFG（Classifier-Free Guidance），每个去噪步骤实际上需要执行 两次 UNet 前向传播（一次有条件、一次无条件），计算量翻倍。\nStep 3：VAE 解码（Latent → Pixel）\n最后，VAE Decoder 将去噪后的 latent（4 通道，128×128）解码为 RGB 图像（3 通道，1024×1024）。\n需要注意的是，SDXL 默认的 VAE 不支持 FP16 推理，会自动 upcast 到 FP32，这是一个可优化的点。\n2.2 UNet 架构细节 SDXL 的 UNet 采用经典的 Encoder-Decoder + Skip Connection 结构，但在内部组件上做了重要改进：\nUNet 结构概览（简化）: Input (4, 128, 128) │ ▼ ┌───────────────────────────┐ │ DownBlock 0 (320ch) │ ──────────────────────────────┐ │ [ResNet + Transformer] │ │ ├───────────────────────────┤ │ │ DownBlock 1 (640ch) │ ─────────────────────┐ │ │ [ResNet + Transformer] │ │ │ ├───────────────────────────┤ │ │ │ DownBlock 2 (1280ch) │ ────────────┐ │ │ │ [ResNet + Transformer] │ │ │ │ ├───────────────────────────┤ │ │ │ │ MidBlock (1280ch) │ │ │ │ │ [ResNet + Transformer] │ │ │ │ ├───────────────────────────┤ │ │ │ │ UpBlock 0 (1280ch) │ ◄────────────┘ │ │ │ [ResNet + Transformer] │ (skip connection) │ │ ├───────────────────────────┤ │ │ │ UpBlock 1 (640ch) │ ◄─────────────────────┘ │ │ [ResNet + Transformer] │ (skip connection) │ ├───────────────────────────┤ │ │ UpBlock 2 (320ch) │ ◄──────────────────────────────┘ │ [ResNet + Transformer] │ (skip connection) └───────────────────────────┘ │ ▼ Output (4, 128, 128) 每个 Transformer Block 内部包含：\nSelf-Attention：latent 特征间的空间注意力，复杂度 O(n²) Cross-Attention：latent 特征与文本嵌入间的交叉注意力 FFN（Feed-Forward Network）：通道维度上的非线性变换 2.3 计算量分布分析 通过分析各组件的计算量占比，可以明确优化的优先级：\n组件 每步执行次数 占比（估算） 备注 UNet Attention (Self + Cross) 1×（或 2× w/ CFG） ~40% 内存带宽密集 UNet Conv + ResNet 1×（或 2× w/ CFG） ~35% 计算密集 UNet FFN 1×（或 2× w/ CFG） ~15% 计算密集 Text Encoder 仅 1 次 \u0026lt;2% 可忽略 VAE Decode 仅 1 次 ~5-8% FP32 时较慢 Scheduler / 其他 CPU 开销 每步 \u0026lt;1% CPU-GPU 同步开销 从上表可以看出：UNet 去噪循环是绝对瓶颈，占总推理时间的 90% 以上。优化 UNet 的执行效率是提升整体性能的关键。\n3. 性能 Profiling 方法论 在开始优化之前，必须先通过系统化的 Profiling 找到真正的性能瓶颈。盲目优化不仅效率低下，还可能适得其反。本节介绍如何使用 NVIDIA 提供的两款核心分析工具。\n3.1 Nsight Systems：系统级时间线分析 Nsight Systems 是系统级的性能分析工具，适用于观察 宏观层面 的执行行为，包括：\nCPU 与 GPU 的执行时间线 CUDA Kernel 的启动和执行时序 内存拷贝操作（HtoD / DtoH） CPU-GPU 同步开销 各阶段（Text Encode / UNet / VAE）的耗时分布 使用方法 # 基本用法：对推理脚本进行 Profiling nsys profile \\ --trace=cuda,nvtx,osrt \\ --output=sdxl_profile \\ --force-overwrite=true \\ python sdxl_inference.py 为了获得更有意义的 Profiling 结果，建议在代码中添加 NVTX 标记 来标注各阶段：\nimport torch import nvtx # 在推理代码中标注关键阶段 with nvtx.annotate(\u0026#34;Text Encoding\u0026#34;, color=\u0026#34;blue\u0026#34;): prompt_embeds = pipe.encode_prompt(prompt) with nvtx.annotate(\u0026#34;UNet Denoising Loop\u0026#34;, color=\u0026#34;red\u0026#34;): for i, t in enumerate(timesteps): with nvtx.annotate(f\u0026#34;Step {i}\u0026#34;, color=\u0026#34;orange\u0026#34;): noise_pred = unet(latents, t, encoder_hidden_states=prompt_embeds) latents = scheduler.step(noise_pred, t, latents).prev_sample with nvtx.annotate(\u0026#34;VAE Decode\u0026#34;, color=\u0026#34;green\u0026#34;): image = vae.decode(latents) 关键观察点 通过 Nsight Systems 生成的时间线视图，我们重点关注以下几点：\n1. GPU 利用率与空闲时间（GPU Idle Gaps）\n在 SDXL 推理的时间线中，典型的观察是：\n每个 UNet step 之间存在微小的 CPU-GPU 同步间隙 Scheduler 的步骤计算在 CPU 上执行，会引入少量空闲 这些间隙单独看很小（μs 级），但累积 2050 步后变得可观 2. Kernel 启动开销（Kernel Launch Overhead）\nSDXL 的 UNet 在 PyTorch eager 模式下，一次前向传播会触发 数百到上千个小 CUDA Kernel。每个 Kernel 的启动有固定开销（~5-10μs），当 Kernel 数量极多时，启动开销不可忽略。\n因此后续可考虑采用 torch.compile 和 TensorRT 等编译优化方式，来减少 Kernel 数量。\n3. 内存拷贝操作\n如果在时间线中发现频繁的 HtoD（Host to Device）或 DtoH 拷贝，说明存在不必要的 CPU-GPU 数据搬运。在正确的实现中，推理过程应基本保持数据在 GPU 上。\n4. CFG 导致的计算翻倍\n启用 CFG 时，Nsight Systems 时间线会清晰地展示每个 step 内 UNet 被调用了两次（batch 维度加倍），这为\u0026quot;部分禁用 CFG\u0026quot;的优化方案提供了直观的依据。\nNsight Systems 分析示例输出 SDXL FP16 Inference Timeline (20 steps, 1024×1024) ===================================================== Phase | Duration | GPU Active | Kernel Count -----------------+----------+------------+------------- Text Encoding | 82 ms | 78 ms | 126 UNet Step (avg) | 185 ms | 181 ms | 847 ├─ Self-Attn | 72 ms | 71 ms | 198 ├─ Cross-Attn | 43 ms | 42 ms | 132 ├─ Conv/ResNet | 58 ms | 57 ms | 389 └─ Other | 12 ms | 11 ms | 128 UNet Total (×20) | 3700 ms | 3620 ms | 16940 VAE Decode | 280 ms | 275 ms | 312 -----------------+----------+------------+------------- Total | 4062 ms | 3973 ms | 17378 GPU Utilization | | 97.8% | Insight：GPU 利用率已经很高（97.8%），进一步优化的方向不在于\u0026quot;让 GPU 更忙\u0026quot;，而在于\u0026quot;让每个 Kernel 跑得更快\u0026quot;或\u0026quot;减少 Kernel 数量\u0026quot;。\n3.2 Nsight Compute：Kernel 级深度分析 Nsight Compute 是 Kernel 级别的性能分析工具，用于深入分析 单个 CUDA Kernel 的执行效率，帮助我们理解瓶颈的微观本质。\n使用方法 # 分析特定范围的 Kernel（避免全量分析耗时过长） ncu --set full \\ --kernel-name \u0026#34;regex:.*attention.*\u0026#34; \\ --launch-skip 100 --launch-count 20 \\ --output sdxl_kernel_profile \\ python sdxl_inference.py 注意：Nsight Compute 的 Profiling 开销极大（单个 Kernel 可能需要重放数十次），建议只分析关键的 Kernel，而非全量扫描。\n关键分析维度 1. Roofline 分析：计算密集 vs 访存密集\nRoofline 模型是判断 Kernel 性能瓶颈最直观的工具：\nPeak FLOPS │ │ ╱ Roofline │ ╱ Performance │ ╱ (FLOPS) │ ╱ ● Attention Kernel（访存密集区） │ ╱ │╱ ● Conv Kernel（计算密集区） └────────────────────────── Arithmetic Intensity (FLOP/Byte) 对于 SDXL 的关键 Kernel：\nKernel 类型 算术强度 瓶颈类型 优化方向 Self-Attention 低 访存密集 Flash Attention、减少显存访问 Cross-Attention 低 访存密集 融合 Kernel、xformers Conv 3×3 高 计算密集 cuDNN 融合、低精度计算 GroupNorm 极低 访存密集 算子融合（与后续激活融合） GELU/SiLU 极低 访存密集 算子融合 2. Occupancy 分析：SM 利用率\nOccupancy 衡量每个 SM（Streaming Multiprocessor）上活跃 warp 数与理论最大值的比值。低 Occupancy 意味着 SM 资源未被充分利用。\nSDXL UNet 中常见的 Occupancy 限制因素：\n寄存器压力：Attention Kernel 使用大量寄存器，限制了每个 SM 可同时调度的 warp 数 共享内存占用：部分 Kernel 使用较多共享内存，同样限制并发度 Kernel: fused_attention_forward_fp16 Theoretical Occupancy: 50.0% Achieved Occupancy: 46.2% Limiting Factor: Registers (128 per thread) Shared Memory: 48 KB / 100 KB available Insight：SDXL 推理的瓶颈在于 寄存器和共享内存的限制导致并发度不够。这一发现对后续的多实例部署策略（MPS）具有重要指导意义。\n3. Memory Throughput 分析\nKernel: attention_softmax_fp16 Memory Throughput: L1 Hit Rate: 82.3% L2 Hit Rate: 64.7% DRAM Bandwidth: 78.2% of peak (675 GB/s) Warp Stall Reasons: Memory Dependency: 43.2% ← 主要瓶颈 Execution: 28.7% Other: 28.1% 对于访存密集型 Kernel，Warp Stall 的主要原因是内存依赖（等待数据从显存加载）。优化方向包括：\n使用 Flash Attention 减少 HBM 访问 算子融合减少中间结果的 store/load 使用低精度（FP16/BF16）减少每次访问的数据量 3.3 Profiling 小结与优化路线图 综合 Nsight Systems 和 Nsight Compute 的分析结果，我们可以得出以下结论：\n发现 影响 对应优化方向 UNet 占 90%+ 推理时间 高 优先优化 UNet GPU 利用率已较高（~98%） 中 重点不在\u0026quot;填满GPU\u0026quot;，而在\u0026quot;每个op更快\u0026quot; Attention 为访存密集型 高 Flash Attention / xformers 大量小 Kernel 启动开销 中 torch.compile / CUDA Graphs / TensorRT CFG 导致计算翻倍 高 部分禁用 CFG VAE 强制 FP32 低 VAE FP16 Fix 寄存器/共享内存限制 Occupancy 中 影响多实例部署策略 基于以上分析，我们规划了如下优化路线图（将在后续两篇文章中展开）：\n优化路线图 ═══════════════════════════════════════════════════════ Layer 1: 基础优化（无损） ├── 数值精度：FP32 → FP16/BF16 ├── VAE FP16 Fix └── 预期收益：~3x 加速 Layer 2: 编译与框架加速（无损） ├── torch.compile (max-autotune) ├── Stable Fast / OneDiff ├── TensorRT └── 预期收益：额外 1.1x~1.25x Layer 3: 组件级优化（可能轻微有损） ├── Tiny VAE ├── 部分禁用 CFG ├── DeepCache └── 预期收益：额外 1.1x~2.3x Layer 4: 吞吐工程优化 ├── Batch 策略 ├── 多实例部署 ├── CUDA MPS └── 目标：最大化 image/s 4. Baseline 建立 在开始优化之前，我们首先建立一个合理的 Baseline 并记录各项指标。\n4.1 FP32 Baseline from diffusers import StableDiffusionXLPipeline import torch pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float32 ).to(\u0026#34;cuda\u0026#34;) prompt = \u0026#34;a photo of an astronaut riding a horse on mars\u0026#34; image = pipe(prompt, num_inference_steps=30, guidance_scale=7.5).images[0] 配置 步长 分辨率 延时（s） 速度（step/s） 显存（GB） FP32 30 1024 16.3 1.84 18.08 FP32 50 1024 26.9 1.85 18.07 4.2 FP16 Baseline（后续对比基准） pipe = StableDiffusionXLPipeline.from_pretrained( \u0026#34;stabilityai/stable-diffusion-xl-base-1.0\u0026#34;, torch_dtype=torch.float16, variant=\u0026#34;fp16\u0026#34; ).to(\u0026#34;cuda\u0026#34;) 配置 步长 分辨率 延时（s） 速度（step/s） 显存（GB） 加速比 FP32 30 1024 16.3 1.84 18.08 1× FP16 30 1024 5.5 5.45 11.24 2.96× FP32 50 1024 26.9 1.85 18.07 1× FP16 50 1024 8.7 5.74 11.24 3.09× FP16 带来了接近 3 倍 的加速和 38% 的显存节省，且图片质量几乎无损。这是最基本也是收益最高的优化。\n后续所有实验均以 FP16 作为 Baseline（30 步，1024×1024，延时 5.5s，显存 11.24GB）。\n4.3 其他精度格式对比 精度 延时（s） 速度（step/s） 显存（GB） 加速比 建议 FP32 16.3 1.84 18.08 1× 仅用于调试 TF32 12.4 2.41 18.08 1.31× 不推荐（加速有限） FP16 5.5 5.45 11.24 2.96× 推荐作为默认 BF16 5.4 5.55 9.62 3.01× 可选（显存更低） TF32 的加速效果远不如 FP16/BF16，因为它仅改变了 Tensor Core 的计算精度，没有减少内存带宽消耗。在 L20 上不建议使用。\nBF16 与 FP16 速度接近，但显存更低（9.62 vs 11.24 GB），适合显存紧张的场景。\n5. 小结与展望 本篇核心收获 SDXL 推理是多阶段 Pipeline：Text Encoding → UNet 去噪循环 → VAE 解码，其中 UNet 占 90%+ 的计算时间 UNet 内部以 Attention 和 Conv 为主：Attention 是访存密集型，Conv 是计算密集型，优化策略不同 Profiling 先于优化：通过 Nsight Systems 看宏观时间线，通过 Nsight Compute 看微观 Kernel 效率 FP16 是最基本的优化：简单切换精度即可获得 ~3× 加速，是所有后续优化的基础 Occupancy 受限于寄存器和共享内存：这一发现将影响第三篇中的多实例部署策略 下篇预告 在第二篇文章中，我们将在 FP16 Baseline 上逐一实践以下优化：\n编译与框架加速：torch.compile、Stable Fast、OneDiff、TensorRT 组件级优化：VAE FP16 Fix、Tiny VAE、禁用 CFG、DeepCache 蒸馏方案：SDXL-Lightning 显存优化：模型 CPU 卸载、批处理加载 每种方案都将给出完整的代码示例、实测数据和适用场景分析，敬请期待。\n参考资料：\nStability AI - SDXL Technical Report Ultimate Guide to Optimizing Stable Diffusion XL NVIDIA Nsight Systems Documentation NVIDIA Nsight Compute Documentation HuggingFace Diffusers - Optimization Guide ","permalink":"https://javey-q.github.io/posts/sdxl%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98%E4%B8%80%E6%A8%A1%E5%9E%8B%E5%89%96%E6%9E%90%E4%B8%8E%E6%80%A7%E8%83%BDprofiling/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e系列导读\u003c/strong\u003e：本系列共三篇文章，渐进式地探讨 Stable Diffusion XL（SDXL）模型的推理优化。第一篇聚焦于问题背景与性能剖析，第二篇展开全面的优化实践，第三篇进行混合组合优化与吞吐工程部署。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-为什么要优化-sdxl-推理\"\u003e1. 为什么要优化 SDXL 推理？\u003c/h2\u003e\n\u003ch3 id=\"11-sdxl-的工程地位\"\u003e1.1 SDXL 的工程地位\u003c/h3\u003e\n\u003cp\u003eStable Diffusion XL（SDXL）是 Stability AI 于 2023 年发布的旗舰级文生图模型，相较于 SD 1.5，它在图像质量、分辨率（原生支持 1024×1024）和语义理解能力上有了质的飞跃。SDXL 已成为开源社区中应用最广泛的高分辨率生成模型之一，广泛用于创意设计、电商素材生成、游戏概念图制作等场景。\u003c/p\u003e\n\u003cp\u003e然而，更强的模型能力伴随着更高的计算开销。SDXL 的参数量达到约 \u003cstrong\u003e3.5B\u003c/strong\u003e（Base UNet 2.6B + Text Encoders 等），相较于 SD 1.5 的 ~0.9B，推理成本大幅增加：\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e指标\u003c/th\u003e\n          \u003cth\u003eSD 1.5\u003c/th\u003e\n          \u003cth\u003eSDXL\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUNet 参数量\u003c/td\u003e\n          \u003ctd\u003e~860M\u003c/td\u003e\n          \u003ctd\u003e~2.6B\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e原生分辨率\u003c/td\u003e\n          \u003ctd\u003e512×512\u003c/td\u003e\n          \u003ctd\u003e1024×1024\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eText Encoder\u003c/td\u003e\n          \u003ctd\u003eCLIP ViT-L/14\u003c/td\u003e\n          \u003ctd\u003eCLIP ViT-L/14 + OpenCLIP ViT-bigG\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFP32 显存占用\u003c/td\u003e\n          \u003ctd\u003e~4 GB\u003c/td\u003e\n          \u003ctd\u003e~18 GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFP16 单次推理延时（30步）\u003c/td\u003e\n          \u003ctd\u003e~1.5s\u003c/td\u003e\n          \u003ctd\u003e~5.5s\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"12-优化的业务价值\"\u003e1.2 优化的业务价值\u003c/h3\u003e\n\u003cp\u003e在生产环境中，推理性能直接决定了：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e用户体验\u003c/strong\u003e：单张图片从 5.5s 降至 2.8s，交互延迟感知有本质差异\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e服务成本\u003c/strong\u003e：GPU 是最昂贵的资源，推理加速 = 同等算力下服务更多用户\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e吞吐能力\u003c/strong\u003e：高并发场景下，从 0.25 image/s 提升到 0.37+ image/s 意味着单卡产能提升近 50%\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"13-实验环境\"\u003e1.3 实验环境\u003c/h3\u003e\n\u003cp\u003e本系列所有实验基于以下硬件与软件环境：\u003c/p\u003e","title":"SDXL 推理极限优化实战（一）：模型剖析与性能 Profiling"}]