想象你打算在古镇开一家“AI 画师餐厅”:客人报一句菜单(文字描述),后厨就要端出一盘独一无二的画。这套后厨有三个人——点单员把客人的话写成订单卡,大厨盯着订单卡反复调味二十到五十遍才肯出锅,装盘师把成品摆得漂漂亮亮端上桌。短短几秒,一道菜就成了。可你真要开店,会立刻撞上一连串问题:灶台(显卡)该买哪个牌子?贵的灶台出菜到底快多少?同一位大厨换套做法(推理框架)能不能更快?一锅同时炒几份(batch size)才最划算?食材要不要提前切好备好(量化)?后厨台面(显存)怎么摆才不打架?这一连串问题,正是文生图服务的推理性能基准测试要回答的——它不关心画多好看,只关心同样的画怎么用最少的钱、最快的速度端上桌。
学会设计和读懂基准测试,你就能在 GPU 选型、框架选择、批次策略上做出数据驱动决策,直接降低文生图服务的成本并提升用户等待体验。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
推理管线速览
想象你刚盘下一家“AI 画师餐厅”。客人进门说“来一幅暮色下的赛博朋克城市”,点单员把这句话浓缩成一张后厨能看懂的订单卡;大厨拿到订单卡,从一团白噪声似的“生面团”开始,反复试味、调整细节,要折腾二十到五十遍才肯出锅;最后装盘师把这份“潜空间”里的半成品摆成一张像模像样的像素画,端上桌。这三个角色的分工,正是 Stable Diffusion 的三段核心管线。
在技术文档里,这三个人分别叫文本编码器(Text Encoder)、UNet 去噪网络、VAE 解码器。文本编码器把一句人话变成一堆数字向量,告诉模型“顾客到底想要什么”;UNet 是最大的体力活,它在潜空间里迭代很多步,每步都把噪声往“像画”的方向推一点点;VAE 解码器负责把处理完的潜空间数据还原成人眼可见的 RGB 图片。
既然知道工序有三段,测性能自然从分别计时开始。一次完整推理的总延迟等于三段耗时之和,其中 UNet 部分要乘上迭代步数 S(通常 20~50 步)。步数越多细节越丰富,但代价是总延迟直线上升:
1. L_total 总出菜时间:一次完整推理的总延迟,从输入提示词到图片生成完毕的全过程耗时。
2. L_enc 与 L_dec 固定环节:文本编码和 VAE 解码各只执行一次,耗时相对固定且占比小,就像点单和装盘。
3. S × L_step 去噪总耗时:UNet 要重复执行 S 次去噪,每次耗时 L_step。步数越多,细节越丰富,耗时也越长,这是优化总延迟的首要目标。
弄清了这三个人,你就拿到了基准测试的第一把尺子:想优化整体出菜时间,先盯住占比最大的环节。这也是后面所有硬件、框架、批次优化主要围着 UNet 转的根本原因。
基准测试方法论
后厨效率体检,只看一道菜端上桌的时间够吗?不够。假设你有一个灶台,炒一份番茄炒蛋要两分钟,但一次能同时炒六份,平均每份只要四十秒。对客人来说,等两分钟和等四十秒体验完全不同;对你来说,高峰时段能不能吞下源源不断的订单,取决于“单位时间能出几道菜”。前者叫延迟,后者叫吞吐量,做基准测试必须分开看。
翻译到 Stable Diffusion 上:延迟指输入一个提示词到生成一张完整图片的总时间,单位是秒/张,直接决定用户等待体感;吞吐量指单位时间内能生成的图片数,常见写法是 it/s(每秒迭代步数)或 images/s(每秒图片数)。两者都很容易在报告里被混为一谈,但它们的优化目标完全相反——降延迟要小批次,提吞吐量要大批次。
一份合格的基准测试报告,至少要交代四件事:什么 GPU、什么推理框架、批次多大、迭代几步。专业测试还会做预热(剔除首次加载的冷启动)、多轮取平均、固定随机种子保证每轮噪声起点相同、准备覆盖不同画风的提示词集,避免“只测一张猫图”的偶然性。吞吐量的核心公式很简单:
1. T 吞吐量:单位时间内端出的菜,即每秒生成的图片数,相当于餐厅翻台率。
2. B 批大小:一锅同时炒几份,batch size 越大,一次处理的图片越多。
3. L_batch 单批延迟:炒完一锅的总用时,从开火到出锅,它随批大小增加而变大。
所以基准测试就是给餐厅做全方位体检:既量单桌等待,又算翻台率,还要规定“每桌都是同样的菜、同样的流程”,报告才可信。这也是你后面做任何选型决策前必须做的第一步。
GPU硬件对比
到了选灶台环节。古镇上有家用小灶——RTX 4060、RTX 4090 这类消费级显卡,也有酒店后厨的猛火灶——A100、H100 这类数据中心 GPU。小灶便宜、省电,但台面小(显存小)、火力输出有限;猛火灶贵得惊人,但台面大、配料摆得开,还能同时开好几口锅,出菜纪律完全不同。
消费级卡和专业级卡的差距,不在“单张图的画质”上——模型参数相同,画出来的图质量完全一致——差别主要体现在三件事:显存容量、显存带宽、多卡扩展能力。SD 的 UNet 权重在 FP16 下约占 3.4GB,激活值则随分辨率和 batch size 增长,消费级 8GB 显存跑大分辨率或大 batch 很容易溢出;专业级卡更高的显存带宽,让每次取权重都快得多。
既然灶台价格差了好几倍,就不能只看绝对速度,而要看性价比——每花一块钱能换回多少产出:
1. E 性价比:单位成本换来的吞吐量,是 GPU 选型的核心指标,相当于“每块钱能买几道菜”。
2. T_gpu 该卡吞吐量:某块 GPU 在相同模型、相同批次下的每秒产出张数,来自基准测试实测。
3. C_gpu 总拥有成本:包含采购价、功耗、机房托管等长期开销,不能只看标价。
选灶台不是越贵越好,而是看“每花一块钱能炒出几道菜”。消费级卡做小规模服务和原型验证,数据中心卡做高并发商业服务,各得其所。
框架加速路线
灶台定了,同一位大厨还可以用不同流派的做法。普通流派(PyTorch 基线)像老老实实按菜谱一步步来,每步都稳妥但偏慢;xFormers 流派像给大厨配了一套特制刀具和颠勺技巧,专门优化最费力的“调味”环节;TensorRT 流派更像把整本菜谱提前背熟,甚至把几个灶眼合并成一条流水线,开火前一切都已排好。
xFormers 的核心是内存高效注意力(memory-efficient attention),它改写 UNet 里的注意力计算,用分块处理降低显存读写,速度通常提升 20%~50%,而且基本不损失画质。TensorRT 则更进一步:把整个 UNet 编译成针对当前 GPU 高度优化的引擎文件,做算子融合(把多个小运算合并成一次内核启动)、自动选低精度 kernel,但首次编译要花几分钟到几十分钟,换 GPU 或改分辨率就要重新编译。
怎么量化流派的差距?用加速比——优化后比优化前快几倍:
1. A 加速比:优化效果的标尺,大于 1 代表变快,数字越大收益越高。
2. L_base 基线耗时:PyTorch 原生实现的单批推理耗时,作为“普通做法”的参照。
3. L_opt 优化后耗时:应用 xFormers 或 TensorRT 之后,同一批任务的推理耗时。
选哪种流派要看场景:海量离线任务,TensorRT 的编译成本摊薄后很划算;在线服务追求灵活性,xFormers 即插即用、兼容多变提示词,是更稳妥的选择。
批次策略调优
大厨能不能一锅炒六份?把六张订单丢进同一锅(一个 batch),模型把六张图一起算。好处是分摊调度开销、让 GPU 算力更饱和;坏处是每一份出锅时间变长,且台面(显存)需要更大。这就是批次大小(batch size)的取舍,也是线上调优最频繁碰到的旋钮。
延迟随 batch size 上升,通常近似线性:batch=4 的单批延迟大约是 batch=1 的 2~3 倍;但吞吐量 = 批大小 ÷ 单批延迟,分子分母同时变,结果是先上升、后趋平甚至下降的曲线,拐点就是甜点区。显存则是硬约束——batch=16 算得再美,显存放不下就一切免谈。最优批次可以由显存上限直接估算:
1. B* 最优批大小:理论上限内吞吐量最高的批次,通常用实测曲线确认。
2. M_avail 可用显存:GPU 总显存减去框架和 CUDA 上下文占用后真正可用的部分。
3. M_sample 单样本开销:处理一张图所需的激活内存,随分辨率和步数变化。
4. floor 向下取整:台面放不下就少放一份,括号里的除法结果只取整数部分。
找到那个“锅刚好装满、台面刚好放下”的批大小,是推理优化工程师的日常。在线交互场景通常固定 batch=1 保低延迟,离线批量场景才拼命往拐点靠。
量化加速方案
备菜是后厨提速的另一大招。食材要提前洗好、切好、装进保鲜盒,大厨炒菜时直接下锅,开火到出锅的时间就短了。模型量化就是这个思路:把参与计算的数字从高精度“降级”为低精度,数据体积变小,搬运变快,计算也变快。
Stable Diffusion 默认用 FP16 半精度,相比 FP32 已经省了一半内存;更进一步是 INT8 量化,把权重和激活从 16 位压到 8 位,理论上内存搬运需求再砍一半。但 INT8 不是简单把参数四舍五入,而是要先准备一小批有代表性的校准图片,统计激活值分布,确定缩放因子和零点,把精度损失压到最小。量化带来的理论加速,主要来自内存带宽的减半:
1. BW 带宽需求:推理中搬运权重和激活消耗的内存带宽,位宽越低,搬运量越小。
2. FP16 与 INT8 位宽比:16 位对 8 位,单个体积正好一半,搬运次数同比例下降。
3. 约等号:实际性能受内核实现、反量化、内存对齐影响,接近但很少正好等于两倍。
备菜能大幅提速,但菜切太碎可能影响口感。量化后必须做画质 A/B 对比和端到端加速比验证,确认没有明显画质崩坏,再决定是否上线。
显存优化管理
最后整理后厨台面。台面上摆着三样东西:常备调料罐(模型权重)、正在处理的半成品(激活值)、临时工具(中间缓存)。台面不够用,要么菜直接做不了(OOM 崩溃),要么得频繁跑仓库取放东西(显存换页),速度断崖式下跌。
推理阶段的显存构成,比训练简单得多:训练要存梯度、优化器状态,推理统统不需要。推理的峰值显存主要由两部分构成——权重和激活值。权重大小固定(FP16 下约 3.4GB),激活值随分辨率、步数、batch size 增长。常见优化手段有三类:激活内存复用(算完一块立即释放)、缓存文本编码结果(同一批提示词只编码一次)、以及前面说的 INT8 权重压缩。峰值显存的模型如下:
1. M_peak 峰值显存:推理过程中台面占用最高的那一刻,超出即 OOM。
2. M_w 权重显存:模型参数的固定占用,FP16 的 UNet 约 3.4GB,量化后可再减半。
3. M_a 激活显存:随分辨率、步数、批次动态起伏的中间数据,是大批次的主要拦路虎。
台面收拾得越清爽,能同时做的菜就越多。所以显存优化和批次策略总是手拉手出现——先摸清显存构成,再决定 batch 开多大,最后用基准数据验证整个方案。
---
AI 应用场景
云厂商文生图 API 定价
阶段:推理服务部署与成本优化
解决的问题:传统做法是拍脑袋选 GPU、按峰值预付费买实例,结果闲时资源浪费严重。用基准测试数据驱动选型——对比 A10、L4、A100 的吞吐量和性价比,再配合 FP16 和 batch=4,就能把单张图的生成成本精确算出来,按量计费。
具体案例:某 API 平台把 A10 换成 L4 并开启 xFormers,batch 设为 4,吞吐量提升 80%,单张图片成本下降约四成,用户平均等待时间反而缩短。
电商商品图批量生成
阶段:离线批量推理
解决的问题:大促前要生成数百万张商品图,单张串行生成要跑好几天。用大 batch(如 8~16)+ TensorRT 引擎离线跑,把吞吐量顶到拐点,一次搞定。基准测试里测出的最优批次和加速比,直接变成批处理管线的配置参数。
具体案例:某电商团队在双十一前用 4 张 A100 跑 TensorRT 引擎,以 batch=8 批量生成白底商品图,三天下线百万张图,整体成本比原来小 batch 方案降低 55%。
实时交互式出图工具
阶段:在线低延迟推理
解决的问题:用户拖拽提示词、调参数时希望 1 秒内看到预览,batch=1 的大吞吐策略在这里不适用。基于基准数据,把延迟拆成编码、去噪、解码三段,针对性用少步数(如 20 步)+ TensorRT + 缓存文本编码结果,把单张延迟压到 1 秒以内。
具体案例:某 Stable Diffusion WebUI 插件团队通过基准测试发现,瓶颈在 UNet 单步耗时,于是换用 TensorRT 并将步数从 30 降到 20,预览延迟从 2.8 秒降到 0.9 秒。
Python 演示
import math class GpuBenchSimulator: def __init__(self, base_latency, per_sample_cost, memory_total, memory_per_sample): self.base_latency = base_latency self.per_sample_cost = per_sample_cost self.memory_total = memory_total self.memory_per_sample = memory_per_sample def latency(self, batch_size): return self.base_latency + self.per_sample_cost * (batch_size - 1) def throughput(self, batch_size): return batch_size / self.latency(batch_size) def max_batch_by_memory(self): return max(1, int(self.memory_total // self.memory_per_sample)) def best_batch(self): cap = self.max_batch_by_memory() best_b, best_tp = 1, 0.0 for b in range(1, cap + 1): tp = self.throughput(b) if tp > best_tp: best_tp, best_b = tp, b return best_b, best_tp def main(): sim = GpuBenchSimulator( base_latency=0.35, per_sample_cost=0.06, memory_total=24, memory_per_sample=4.5, ) print("batch | 延迟(秒/批) | 吞吐量(张/秒)") for b in [1, 2, 4, 8, 16]: if b > sim.max_batch_by_memory(): print(" %3d | 显存不足 | --" % b) continue print(" %3d | %.2f | %.2f" % (b, sim.latency(b), sim.throughput(b))) b, tp = sim.best_batch() print("最佳批次: %d, 最大吞吐量: %.2f 张/秒" % (b, tp)) if __name__ == "__main__": main()
return self.base_latency + self.per_sample_cost * (batch_size - 1)模拟“炒一锅”的耗时随份数线性增长,就像 batch 增大导致单批延迟变长。return batch_size / self.latency(batch_size)翻台率等于一锅几份除以一锅耗时,这正是正文的吞吐量公式。max(1, int(self.memory_total // self.memory_per_sample))用显存上限除以单份占用,算出台面最多放几份。for b in range(1, cap + 1):从 1 份到满额逐个试探,实测吞吐量曲线,而不是拍脑袋定批次。if __name__ == "__main__":脚本可执行入口,运行后直接输出每个批次的延迟和吞吐量对比表。
常见误区
误区我看到一份基准报告写着“每秒迭代 20 次”,心想那每秒能出 20 张图,这卡真快。
正解it/s 说的是 UNet 单步迭代的速度,而一张图要迭代 20~50 步,还要加上文本编码和 VAE 解码。每秒 20 it/s 意味着单步 50 毫秒,完整一张图要 1~2.5 秒。就像大厨颠勺一秒钟颠十下,不代表十道菜一秒出齐。看报告要先分清 it/s 和 images/s 两个单位。
为何常见:社区讨论 SD 性能时铺天盖地用 it/s,小白很容易把这个中间指标当成最终出图速度。
误区既然一锅炒六份比一份省事,那 batch 开到 32、64 不是爽翻?显存不够就加钱上大显存卡。
正解吞吐量曲线会先上升后趋平甚至下降——当批延迟的增长快过批次增长时,吞吐量反而下滑;显存是硬顶,OOM 直接崩。正确做法是实测找到拐点,同时区分场景:在线交互用小 batch 保低延迟,离线批量才用大 batch 冲吞吐量。
为何常见:“批量=便宜”的直觉是对的,但忽略了延迟增长不是免费午餐,盲目加大批次会撞上收益递减和显存墙。
误区TensorRT 加速比那么高,无脑上 TensorRT,所有场景都快,xFormers 可以扔了。
正解TensorRT 强在编译期算子融合和低精度内核,但首次编译要几分钟到几十分钟,换 GPU、换分辨率、换模型版本都要重新编译;对动态形状、频繁变化提示词的在线服务,xFormers 的灵活性和通用性反而更好。加速比是相对量,必须结合基线和场景判断。
为何常见:只看单条 Benchmark 的加速比数字,忽略了编译成本和适用边界,把“特定条件下的快”当成了“全场景的快”。
误区量化后内存少一半、速度翻倍,画质肉眼看差不多,那把线上服务全部换成 INT8 就完事了。
正解INT8 需要精心挑选校准集,校准集分布覆盖不全,某些风格会崩坏;部分算子没有 INT8 优化实现,端到端速度反而更慢;量化后必须做画质 A/B 和真实加速比验证。跟备菜一样,切好的菜省时间,但刀工差会把食材毁掉。
为何常见:FP16 相对 FP32 接近无损的先验被错误迁移到 INT8 上,忽略了校准过程和潜在的精度回退。
面试题
Q1. 下面哪项是文生图推理吞吐量的标准单位?
- A.每秒生成的图像张数← 正确答案
- B.每张图像的推理延迟
- C.UNet 单步迭代耗时
- D.模型参数量的大小
答案:A。吞吐量衡量“单位时间产出多少张图”,单位就是张/秒。B 是延迟的单位,C 是单步耗时,D 是模型规模,都不描述产出速率。就像翻台率看的是每小时接待几桌,不是单桌吃多久。
Q2. 在 SD 推理总延迟中,通常占比最大的阶段是?
- A.文本编码器的编码耗时
- B.UNet 的迭代去噪过程← 正确答案
- C.VAE 潜空间图像解码
- D.输入图像尺寸的缩放
答案:B。UNet 要迭代 20~50 步,每步都有完整的前向计算,总耗时稳居第一。文本编码和 VAE 解码各只执行一次,占比很小。这就像大厨反复调味二十遍,点单和装盘再慢也追不上。
Q3. 将 batch size 从 1 提升到 4,通常发生什么?
- A.单批推理延迟反而下降
- B.吞吐量上升但单图延迟升← 正确答案
- C.显存占用基本保持不变
- D.生成图的画质必然下降
答案:B。批次增大让单批延迟变长(约 2~3 倍),但产出从 1 张变 4 张,所以吞吐量通常上升。显存一定增加,画质由模型决定与批次无关。一锅炒四份,上菜总时长增加,但翻台率提高了。
Q4. 下列哪些属于 xFormers 的典型优化手段?
- A.内存高效注意力机制← 正确答案
- B.算子融合与图编译
- C.降低注意力计算复杂度← 正确答案
- D.适用于 Transformer 模型← 正确答案
答案:ACD。xFormers 的核心是内存高效注意力,通过分块计算降低 attention 的复杂度和内存读写,天然适用于 Transformer 架构。B 的算子融合和图编译是 TensorRT 的拿手好戏,不是 xFormers 的主打。
Q5. TensorRT 加速的关键机制不包括哪项?
- A.编译为独立优化引擎
- B.利用低精度推理加速
- C.算子融合降低内核开销
- D.自动改写提示词提升画质← 正确答案
答案:D。TensorRT 做的是“编译引擎 + 低精度 kernel + 算子融合”,它优化计算速度,完全不碰提示词内容,更管不了画质。D 属于应用层功能,与推理引擎无关,就像换一口好锅不会改变菜谱本身。
Q6. 推理阶段显存主要由哪两部分构成?
- A.权重参数与激活值← 正确答案
- B.权重与优化器状态
- C.反向传播梯度缓存
- D.训练数据集样本
答案:A。推理没有反向传播,不需要梯度和优化器状态,那是训练阶段的事;数据集也不常驻 GPU 显存。推理的峰值显存 = 权重 + 激活值,相当于后厨台面上只有调料罐和半成品,没有账本。
Q7. 关于 INT8 量化,下列说法正确的有?
- A.需要校准集统计激活分布← 正确答案
- B.带宽需求约为 FP16 的一半← 正确答案
- C.量化后模型必然变慢
- D.可能引入精度损失需验证← 正确答案
答案:ABD。INT8 的缩放和零点要从校准集中统计激活分布来确定;位宽从 16 降到 8,带宽需求理论上减半。但部分算子缺少高效 INT8 实现,端到端不一定变快,且可能损失精度,必须 A/B 验证。C 说“必然变慢”太绝对。
Q8. 做推理延迟基准测试时,为得到稳定数据应怎么做?
- A.只运行一次记录结果
- B.预热后再多次运行取平均← 正确答案
- C.每次更换随机种子再对比
- D.不预热直接测首次结果
答案:B。首次运行包含模型加载和 CUDA 上下文初始化,必须预热剔除;多次运行取平均能消除系统噪声。C 换种子是反面做法,会引入画质差异干扰计时。就像餐厅体检不会在刚开门没生火时测,要等后厨进入稳定状态。
Q9. 消费级 GPU 与数据中心 GPU 相比,主要差距通常不在于?
- A.显存容量的大小差距
- B.显存带宽的规模差距
- C.单张图像画质的高低← 正确答案
- D.并行计算核心数量
答案:C。模型权重和推理算法相同,画质由模型本身决定,与用哪块 GPU 无关。消费级和专业级的主要差距在显存容量、带宽和算力规模,这也是基准测试要量化对比的维度。灶台火力有大小,但菜谱相同,菜的味道不会变。
Q10. 文生图服务降本增效时,数据驱动的优化手段包括哪些?
- A.按峰谷动态调整实例数量← 正确答案
- B.大批量离线与低延迟在线分流← 正确答案
- C.用基准数据指导 GPU 选型← 正确答案
- D.无限增大批次直到显存溢出
答案:ABC。流量弹性伸缩、离线批量与在线实时分场景配置、用性价比数据选 GPU,都是典型的成本优化手段。D 无视显存上限和收益递减,必然导致 OOM 或吞吐量下滑,是反面教材。