video_gen_physics_backup / docs /ctrlworld_pruning_analysis.md
doanh25032004's picture
Backup source tree of video_gen_physics (2026-07-31T14:21:08Z)
ec0a9aa verified
|
Raw
History Blame Contribute Delete
7.29 kB

Ctrl-World Token-Pruning Analysis (SiTo / ITM)

Empirical study of inference-time token pruning (SiTo, Importance Token Merge) applied to the Ctrl-World action-conditioned video world model. Records the profiling methodology, measurements, and conclusions for later write-up.

All numbers below are on single_arm multiview, makovian subset, episode_000255, num_inference_steps=25, bf16, one H100-class GPU. Absolute wall times were collected while the GPU was shared with other jobs, so treat them as relative comparisons (dense baseline re-measured in every batch).


1. Model / workload

  • Ctrl-World = SVD-based (Stable Video Diffusion) UNet, action-conditioned, autoregressive replay. 3-view multiview, 320x192-per-view stacked (height×3).
  • Per episode: interact_num autoregressive segments × num_inference_steps denoise steps × UNet forward. guidance_scale=1 by default (NO CFG).
  • Attention layers: 64 diffusers AttnProcessor slots. Spatial self-attention sequence lengths observed: 2880 (72×40), 720 (36×20), 180 (18×10); temporal attention sequence length 11 (num frames), with batch = H·W.

2. Profiling methodology

  • Wrapped unet.forward, vae.encode/decode with CUDA-synced timers.
  • Registered forward pre/post hooks on BasicTransformerBlock, TemporalBasicTransformerBlock, ResnetBlock2D/TemporalResnetBlock to get per-module-type time.
  • Instrumented SVDSiToAttnProcessor._should_use_sito to count fired seq_lens.
  • Standalone micro-benchmarks: isolated to_q/k/v + SDPA + to_out, full BasicTransformerBlock, and full TransformerSpatioTemporalModel forwards, dense vs pruned, with plan prepare() timed separately.

3. Where the time goes

Episode wall-time split:

Component Share
UNet forward ~64%
VAE decode ~8%
VAE encode ~1%
Python/loop/mediapy ~27%

UNet-internal split (per-module-type hooks):

UNet component Share of UNet
Spatial BasicTransformerBlock ~16%
TemporalBasicTransformerBlock ~22%
ResNet (2D + temporal) ~31%
proj_in/out, time embed, other ~31%

Attention-only split: temporal attn (seq_len=11) ≈57% of attention time; spatial attn (2880/720/180) the remaining ≈43%. SiTo/ITM only touch spatial self-attention ≈ 7% of total wall time.

4. Per-layer prune profitability (micro-bench, dense vs pruned)

Isolated self-attention (to_q/k/v + SDPA + to_out), plan precomputed:

seq_len C prune speedup (SDPA-only)
2880 320 0.45 1.71x
720 640 0.35 0.72x (slower)
180 1280 0.20 0.76x (slower)
11 (temporal) 320 0.30 0.75x (slower)

Only the largest stage (2880) benefits. Smaller stages have few tokens / high channels; the index_select + scatter bookkeeping outweighs the attention FLOPs.

5. The two overhead killers

  1. prepare() cost > attention it prunes.

    • SiTo prepare() ≈ 0.67 ms/call (normalize, mean, score matmul, argmax, patch layout, similarity recover), vs a dense 2880 attention ≈ 0.42 ms.
    • ITM prepare()11.7 ms/block (randperm, full argsort, src×dst similarity matmul, scatter). Catastrophic.
    • Mitigation implemented: plan caching across denoise steps (plan_recompute_every; the plan is spatial-structure-driven and stable), plus a single index_select recover (precomputed gather_from_kept) replacing two scatters + a gather. Cache-once (per episode) lifts the 2880 layer from 0.42x → 1.26x; 720/180 stay <1x even cached.
  2. ITM forces guidance_scale → 2. ITM's importance = |cond − uncond| heat map needs classifier-free guidance, which doubles the UNet batch. Dense runs at guidance_scale=1. This alone makes vanilla ITM ~1.5–2x slower than dense regardless of pruning. Mitigation: block-hold self_importance mode derives importance from hidden-state feature L2 magnitude → runs at CFG=1.

6. Approaches tried & results

Dense baseline (25 steps): ~156–267 s depending on GPU sharing; PSNR ≈ 21.3.

Approach Speedup PSNR Note
SiTo per-attn, cache=5 0.94x 19.8 prepare still too frequent
SiTo per-attn, cache-once, ratio1, patch4 ~1.0x 21.4 quality kept, no speedup
ITM per-attn (CFG=2) 0.44x 20.5 CFG doubling dominates
ITM block-hold, self-importance, CFG=1 0.9x 16–20.7 attn+FF only, temporal untouched
ST-hold keep0.9, stage2880 only ~1.0x 18.9 quality-leaning default
ST-hold keep0.8, all stages 1.04x 16.4
ST-hold keep0.5, all stages 1.16x 13.7 fastest, lossy

Spatio-temporal prune-once-hold (the only >1.1x path)

Key structural fact: the temporal block reshapes to (batch·H·W, T, C), i.e. its effective batch = number of spatial tokens. Pruning spatial tokens ONCE at the TransformerSpatioTemporalModel input and holding them compressed through BOTH the spatial and temporal blocks (unmerge only before proj_out) shrinks ~38% of UNet instead of ~16%. Implemented in methods/prunning/SiTo/spatiotemporal_hold.py (--sito_spatiotemporal_hold).

  • Real speedup achieved: up to 1.16x at keep_ratio 0.5.
  • Quality collapses: pruning before the temporal block corrupts temporal dynamics; even keep_ratio 0.9 (drop 10%) gives PSNR ~18.9 and no real speed.
  • similarity_recover=True (recover pruned token from most-similar kept token, cosine) adds ≈ +3.7 PSNR at equal speed vs nearest-index recover — keep it on.
  • No config is simultaneously >1.1x and PSNR>18. Clear speed↔quality Pareto, no free lunch.

7. Ceiling argument (why 16% caps it)

If spatial blocks are sped k× and are fraction f of UNet, UNet speedup = 1/((1−f) + f/k). With f≈0.16 and an optimistic per-block k≈1.2, UNet≈1.03x; with k≈1.95 (aggressive prune), UNet≈1.08x → episode ≈1.05x (UNet is 64% of wall). Extending to spatial+temporal (f≈0.38) is what unlocks >1.1x, but at the quality cost documented above.

8. Recommendation

  • Token pruning (SiTo/ITM) on Ctrl-World: quality-preserving ⇒ ~break-even speed; real speedup ⇒ large quality loss. Fundamental to the small SVD UNet.
  • For speedup that preserves quality, use whole-UNet step caching (DiCache/WorldCache) which skips entire UNet forwards (attacks the 64%). DiCache @25 steps kept PSNR ≈ 21.2 (≈ dense) in preliminary tests.
  • Keep SiTo/ITM as measured benchmark data points (per project policy: run every method even when its speedup is weak/negative).

9. Reproduce

# dense baseline (25 steps)
python scripts/infer_single_arm_multiview_ctrlworld.py ... --num_inference_steps 25

# SiTo per-attn, quality-preserving cache-once (break-even speed)
... --use_sito --sito_max_downsample_ratio 1 --sito_plan_recompute_every 999999 \
    --sito_patch_h 4 --sito_patch_w 4 --sito_prune_ratio 0.7

# Spatio-temporal hold (fast, lossy)
... --use_sito --sito_spatiotemporal_hold --sito_st_keep_ratio 0.5 --sito_max_downsample_ratio 4

Run scripts: scripts/run_single_arm_multiview_ctrlworld_{sito,itm}.sh. Related rules: .cursor/rules/ctrlworld-pruning-insights.mdc, .cursor/rules/acceleration-benchmark.mdc.