microsoft / microsoft/Mage

[BUG/Performance] CFG vector collapse, Turbo trajectory drift, and OOM in packed multi-image sequences

Open
#20 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.6k
Forks
174
PR merge metrics
No merged PRs in 30d

Description

Summary

A deep-dive technical audit of Mage-Flow (pipeline.py and _attn_backend.py) identified five critical issues across sampling mechanics, ODE trajectory scheduling, memory safety, and CUDA kernel dispatching.

Below is a combined analysis of the root causes and their corresponding fixes.


1. Problem Description & Root Cause Analysis

1.1 CFG Renormalization Vector Collapse
  • Location: pipeline.py:_velocity()
  • Root Cause: Velocity vectors are rescaled per-token using torch.norm(cond, dim=-1). In uniform or flat image regions, the conditional velocity norm approaches zero, gating the CFG vector near zero. Furthermore, per-token normalization non-uniformly skews vector directions across spatial tokens.
  • Impact: Feature collapse in uniform regions and spatial directional inconsistency.
1.2 Static Shift Trajectory Drift in Few-Step Turbo Schedules
  • Location: pipeline.py:build_scheduler()
  • Root Cause: Applying a static shift ($6.0$) to linear base sigmas for 4-step Turbo inference produces a steep drop on step 1 ($\sigma = 1.0 \to 0.667$, a 33% jump) where ODE curvature is highest.
  • Impact: Trajectory drift and generation quality loss during low-step sampling schedules.
1.3 Memory Explosion During Packed Multi-Image batch_cfg
  • Location: pipeline.py:_build_pack_ctx()
  • Root Cause: batch_cfg=True duplicates sequences across image and text modalities (e.g., $16,384$ tokens for dual $1024 \times 1024$ image pairs). FlashAttention workspace allocation scales quadratically ($O(N^2)$).
  • Impact: Out-Of-Memory (OOM) exceptions on 24GB GPUs (RTX 3090/4090).
1.4 Sequential Python Loop Overhead in Native SDPA Fallback
  • Location: _attn_backend.py:_resolve_sdpa()
  • Root Cause: When FlashAttention is unavailable, fallback SDPA execution dispatches a Python for-loop across packed sequences ($4 \times 28 \times 30 = 3,360$ separate CUDA kernel launches per generation).
  • Impact: Severe CPU-bound execution bottlenecks (8–12× slowdown) on non-A100/H100 hardware.
1.5 Unchecked FlashAttention-4 Dispatch on Ampere GPUs
  • Location: _attn_backend.py:_resolve_fa4()
  • Root Cause: Window size parameters are normalized without validating CUDA compute capability ($< \text{SM90}$).
  • Impact: Silent execution fallbacks or opaque CUTE DSL errors on SM80/SM86 architectures (A100 / RTX 3090).

2. Proposed Solutions & Refactored Code

import math
import torch
import logging
from typing import List

logger = logging.getLogger("MageFlowFixes")

# Solution 1: Global norm rescaling for CFG (direction-preserving)
def compute_velocity_field_refactored(unc: torch.Tensor, cond: torch.Tensor, cfg_scale: float, renorm: bool = True) -> torch.Tensor:
    comb = unc + cfg_scale * (cond - unc)
    if not renorm:
        return comb
        
    cond_norm = torch.norm(cond.float(), dim=-1, keepdim=True).mean(dim=1, keepdim=True)
    comb_norm = torch.norm(comb.float(), dim=-1, keepdim=True).mean(dim=1, keepdim=True)
    return comb * (cond_norm / (comb_norm + 1e-6))

# Solution 2: Cosine sigma schedule for low-step Turbo ODE trajectories
def build_turbo_sigmas_refactored(num_steps: int, shift: float = 6.0) -> List[float]:
    if num_steps <= 8:
        steps = torch.linspace(0, math.pi / 2, num_steps + 1)
        base_sigmas = (1.0 - torch.sin(steps[:-1])).tolist()
    else:
        base_sigmas = torch.linspace(1.0, 1.0 / num_steps, num_steps).tolist()
        
    return [(shift * s) / (1.0 + (shift - 1.0) * s) for s in base_sigmas] + [0.0]

# Solution 3: Memory guard for batch_cfg
def should_enable_batch_cfg(total_tokens: int, n_heads: int, head_dim: int, device: torch.device) -> bool:
    if not torch.cuda.is_available():
        return False
    free_mem, _ = torch.cuda.mem_get_info(device)
    estimated_bytes = (total_tokens ** 2) * n_heads * head_dim * 8
    return estimated_bytes < (0.70 * free_mem)

# Solution 5: Architecture-aware FlashAttention dispatch
def resolve_attn_backend_safe():
    if not torch.cuda.is_available():
        return "sdpa"
    
    major, _ = torch.cuda.get_device_capability()
    if major >= 9:
        return "fa4"
    elif major >= 8:
        return "fa2"
    return "sdpa"

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading pipeline.py at _velocity(), build_scheduler(), and _build_pack_ctx(), then inspect _attn_backend.py at _resolve_sdpa() and _resolve_fa4(). Reproduce the four-step schedule, packed multi-image batch_cfg, fallback attention, and Ampere cases before evaluating the proposed changes; done means the listed quality, memory, dispatch, and performance failures are addressed without regressions.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, pytorch
Domain
machine-learning, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.