Skip to content

Dion2/NorDion2 FSDP2 padding regression #96

Description

@alint77

Problem

Dion's whole point is a cheaper orthogonalization step: select only a
fraction (default 0.25) of each matrix's rows and run Newton-Schulz on that
submatrix. Under FSDP2 row-sharding this saving is silently erased.

In megabatch_orthogonalize_async (dion/megabatch_base.py), every rank's
selected rows are padded back up to the full per-rank shard before the
all-to-all:

padded_local_size = ceil(global_comm_dim_size / world_size)   # the FULL shard
# e.g. 5120-row weight, 4 ranks: selected 320 rows -> padded back to 1280

Because select_dim == shard_dim (both -2/rows), the selection dimension is
exactly the dimension the pad rule re-inflates. Result: the assembled matrix is
full size (~¾ zero rows), so comm volume and NS GEMM cost are identical to
NorMuon
— Dion2's fraction buys nothing here.

Not a correctness bug (zero rows contribute nothing to UᵀU and are narrowed off
after the second all-to-all). It is a performance regression: Dion2 pays the
selection overhead (topk, gather, scatter, ~244-kernel tail) and collects none of
its compute/comm reward.

How it was identified

  • GPU kernel breakdown of nordion2_gramns vs normuon_gramns (5120/8L, bs1
    traces): both spend ~63% in the same gramNS GEMMs; nordion2's NS is only
    modestly cheaper (49 vs 65 ms), not the ~4x expected from fraction=0.25.
  • Trace shapes confirmed it: nordion2's all_to_all chunks and
    gemm_symmetric inputs are full [*, 5120, 5120], byte-identical comm
    (2500 MB / 6 calls) to normuon — the selection never shrank the matrix.
  • git blame + history traced the padding to the megabatch path and showed the
    original Dion2 did not do this.

When it was introduced

The original Dion2 (Kwangjun Ahn) had a dedicated distributed path that
communicated and orthogonalized only the selected submatrix — the old
dion2_update_batch_async sized its all-to-all buffers to U_selected and
carried the literal comment # Only submatrix is orthogonalized!. No pad to
global size.

The regression was introduced by the megabatch refactor that moved Dion2
onto NorMuon's shared comm path, then hardened by later padding fixes:

date commit what
2025-11-13 5ca6fa5 Kwangjun Ahn (#15) original Dion2; selected-submatrix-only comm + NS
2026-03-27 4266bd0 John Langford Refactor Dion2 to use megabatching — Dion2 joins the shared path (regression starts here)
2026-03-27 e4cc9b2 John Langford Dion2 + NorMuon share the megabatch base class
2026-05-04 e8e1c6a John (#74) pad to ceil(global/world) to fix an FSDP2 empty-shard NCCL hang (makes the full-size inflation explicit)
2026-05-06 ff0fb99 (#75) / 4dc5728 (#77) precompute + harden padded_local_size

Root cause

megabatch_orthogonalize_async is shared between full-shard callers (Muon/
NorMuon, where padding to ceil(global/world) is correct and necessary to avoid
the all-to-all hang) and selected callers (Dion2/NorDion2). The pad rule has no
notion that the selected input was already subsampled, so it re-inflates the
selection to the full shard. The original per-optimizer path avoided this by
sizing comm to k = ceil(fraction * rows) directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions