执行摘要
- 一句话:预计算 Ideogram4 去噪元数据移除逐步开销
- 推荐动作:该 PR 是一个典型的“移动不变计算出循环”的优化案例,代码量小、逻辑清晰、profile 证据充分,适合作为性能优化参考。建议精读
_prepare_denoising_loop 和 _run_denoising_step 的对比,理解如何通过预计算消除逐步开销。
功能与动机
PR body 指出 profiler 跟踪显示 denoising 步内重复出现 aten::nonzero(690.480 ms / 8 calls)和 aten::cumsum 等掩码元数据构造操作,这些操作属于 host 可见的同步开销,可以通过预计算一次性消除。目标是将这些计算移至 _prepare_denoising_loop 阶段,使 denoising 循环主体更纯粹地执行 BF16 cast、GEMM、FlashAttention 和 RMSNorm 等 GPU 密集型计算。
实现拆解
- 预计算 schedule 值和 delta(
ideogram.py 的 _prepare_denoising_loop):在去噪循环准备阶段调用 schedule(step_intervals) 获得 schedule_values,并计算 schedule_deltas = schedule_values[:-1] - schedule_values[1:]。将这两个张量存入 ctx.extra,替换原来存储的 schedule 函数引用和 step_intervals 张量。
- 预构建 attention mask 和 varlen 元数据(同方法):根据
data["segment_ids"] 和 neg_segment_ids 计算 attn_mask / neg_attn_mask(形状为 [batch_size, seq_len] 的布尔张量),并调用 build_varlen_mask_meta 得到 attn_mask_meta / neg_attn_mask_meta,一并放入 ctx.extra。
- 简化 denoising 步内读取(
_run_denoising_step):从 ctx.extra 中直接取出 schedule_values 和 schedule_deltas,通过 t_val = schedule_values[i + 1]; t = t_val.expand(z.shape[0]) 替换原先的 float(schedule(...).item()) 重复求值。同时将预计算的 attn_mask 和 attn_mask_meta 传入 transformer forward。
- Transformer forward 兼容性调整(
ideogram.py 中 Ideogram4Transformer2DModel.forward):签名增加两个可选参数 attn_mask: torch.Tensor | None = None 和 attn_mask_meta: dict | None = None。当它们为 None 时,仍以原有逻辑从 segment_ids 构造掩码和元数据,保持向后兼容。
关键文件:
python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/ideogram.py(模块 扩散流程;类别 source;类型 core-logic;符号 _prepare_denoising_loop, _run_denoising_step): Pipeline 核心逻辑:在 _prepare_denoising_loop 中预计算 schedule 值和 attention mask 元数据,并修改 _run_denoising_step 使用预计算值,是优化的主要载体。
python/sglang/multimodal_gen/runtime/models/dits/ideogram.py(模块 扩散模型;类别 source;类型 data-contract;符号 Ideogram4Transformer2DModel.forward): Transformer 模型定义:forward 签名增加可选参数以接受预计算的 mask 和元数据,同时保留 fallback 路径确保兼容性。
关键符号:Ideogram4DenoisingStage._prepare_denoising_loop, Ideogram4DenoisingStage._run_denoising_step, Ideogram4Transformer2DModel.forward
关键源码片段
python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/ideogram.py
Pipeline 核心逻辑:在 _prepare_denoising_loop 中预计算 schedule 值和 attention mask 元数据,并修改 _run_denoising_step 使用预计算值,是优化的主要载体。
# ideogram.py (pipeline stage)
# 在进入去噪循环前一次性预计算所有步间不变的元数据
def _prepare_denoising_loop(
self, batch: Req, server_args: ServerArgs
) -> DenoisingContext:
# ... 初始化 schedule、step_intervals、guidance_schedule ...
# ★ 预计算 schedule 值和每步 delta,避免循环内反复求值
schedule_values = schedule(step_intervals) # 张量,形状 [num_steps+1]
schedule_deltas = schedule_values[:-1] - schedule_values[1:] # 相邻步差值
# ... 构造 text_z_padding、neg_position_ids 等 ...
# ★ 预计算 attention mask 及其 varlen 元数据(正 + 负)
attn_mask = data["segment_ids"] > 0
neg_attn_mask = neg_segment_ids > 0
ctx.extra.update({
"ideogram4_schedule_values": schedule_values, # 原先是函数引用 + intervals
"ideogram4_schedule_deltas": schedule_deltas,
"ideogram4_guidance_schedule": guidance_schedule,
"ideogram4_text_z_padding": text_z_padding,
"ideogram4_attn_mask": attn_mask,
"ideogram4_attn_mask_meta": build_varlen_mask_meta(attn_mask), # 一次性构造
# ... neg 版本同理 ...
"ideogram4_neg_attn_mask": neg_attn_mask,
"ideogram4_neg_attn_mask_meta": build_varlen_mask_meta(neg_attn_mask),
})
return ctx
def _run_denoising_step(
self, batch: Req, step: DenoisingStepState, ctx: DenoisingContext
) -> DenoisingStepState:
# ... 从 ctx.extra 中直接取出预计算值 ...
schedule_values = ctx.extra["ideogram4_schedule_values"]
schedule_deltas = ctx.extra["ideogram4_schedule_deltas"]
guidance_schedule = ctx.extra["ideogram4_guidance_schedule"]
# ...
i = step.t_int
t_val = schedule_values[i + 1] # 直接索引,取代 schedule(step_intervals).item()
t = t_val.expand(z.shape[0]) # expand 替代逐元素 full
# ... 调用 transformer forward 时传入预构建的 mask 元数据 ...
pos_z = torch.cat([ctx.extra["ideogram4_text_z_padding"], z], dim=1)
# ...
model_output = self.model[
"unet"
].forward(
llm_features=neg_llm_features,
x=neg_z,
t=t,
position_ids=data["position_ids"],
segment_ids=data["segment_ids"],
indicator=data["indicator"],
attn_mask=ctx.extra["ideogram4_attn_mask"], # 传递预计算掩码
attn_mask_meta=ctx.extra["ideogram4_attn_mask_meta"],
)
# ...
python/sglang/multimodal_gen/runtime/models/dits/ideogram.py
Transformer 模型定义:forward 签名增加可选参数以接受预计算的 mask 和元数据,同时保留 fallback 路径确保兼容性。
# ideogram.py (transformer model)
# forward 签名增加可选参数
def forward(
self,
*,
llm_features: torch.Tensor,
x: torch.Tensor,
t: torch.Tensor,
position_ids: torch.Tensor,
segment_ids: torch.Tensor,
indicator: torch.Tensor,
attn_mask: torch.Tensor | None = None, # ★ 新增可选参数
attn_mask_meta: dict | None = None, # ★ 新增可选参数
**kwargs,
) -> torch.Tensor:
# ... 类型转换和投影 ...
cos, sin = self.rotary_emb(h, position_ids)
# 构建注意力掩码:如果外部传入了预计算版本则直接使用,否则内部 fallback
if attn_mask is None:
attn_mask = segment_ids > 0
if attn_mask_meta is None:
attn_mask_meta = build_varlen_mask_meta(attn_mask) # 仅在需要时构造
for layer in self.layers:
h = layer(
h,
cos=cos,
sin=sin,
adaln_input=adaln_input,
attn_mask=attn_mask,
attn_mask_meta=attn_mask_meta,
)
return self.final_layer(h, c=adaln_input).to(torch.float32)
评论区精华
本 PR 无实质 review 讨论。仅有一位自动化 reviewer(gemini-code-assist)给出“无 review 评论”的确认,以及维护者 mickqian 的两次 approve。
风险与影响
关联脉络
参与讨论