[Perf] Admit HC row ownership with the qualified MTP GEMM policy

Communicate every HC prefill partial in FP32 and preserve the established process GEMM policy so MTP packed decode remains admitted. Qualify existing true-reduction state/graph cases without relaxing the failed FP16 drift limit; full-model resource and verifier gates remain pending.

Assisted-by: Codex
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com>
This commit is contained in:
yangzhuxinyzx committed 2026-10-15 07:17:48 +08:00
1 parent c445d8982c
commit 3baabea3d8
6 files changed
+114 -5

No files matched your search

@@ -100,6 +100,33 @@ the requested allocator operation; models without a host-QSA consumer do
not apply it. CPU admission/hash tests pass; GPU graph/cache-pressure and
matched model/headroom/performance qualification are pending.
## HC prefill admission under MTP
Both allocator384 and512 candidates fail actual prefill while still leaving
physical bytes free. The cached fallback requests144/160MiB full residual
temporaries. Inspection finds that the HC row-owner admission additionally
requires FP16 reduced-precision reductions to be disabled. The established
MTP decode policy retains them, so requesting HC sequence prefill alone did
not select the measured low-memory path. The earlier whole-model result is
not evidence that row ownership executed; its stage tests explicitly set
the different reduction policy.
The repair preserves the existing process GEMM policy and MTP packed routes.
HC prefill partials communicate in FP32 for attention's single contribution
as well as MoE's separate contributions, then retain the existing FP16
combine/projection interface. Admission may therefore preserve either
qualified reduction setting; FP16 accumulation and high-level captured
prefill remain excluded. A route log distinguishes actual execution from
the requested flag. Other model/shape/topology guards remain in place.
The true-reduction FP16 transport screen fails the original48-step factor
allowance at8192 rows and is retained as failed. FP32 transport passes the
unchanged independent-reference and48-step allowances at7344/8192 on all
four ranks. Model-adapter padding, changed-input capture and compilation
checks pass4096/4097/7344/7345/8192 with the actual MTP reduction setting.
CPU admission/owner tests pass43 cases. Whole-model quality, selected route,
memory and verifier curves remain required before promoting this repair.
## Rejected screens
Warp-aggregated histograms regress random inputs. Warp-local emission
@@ -142,6 +142,10 @@ def main():
)
parser.add_argument("--tokens", type=int, nargs="+", default=[7344, 8976, 11424])
parser.add_argument("--drift-steps", type=int, default=0)
parser.add_argument("--reduced-precision", action="store_true")
parser.add_argument(
"--drift-communication", choices=("fp16", "fp32"), default="fp16"
)
args = parser.parse_args()
rank = int(os.environ["LOCAL_RANK"])
torch.accelerator.set_device_index(rank)
@@ -149,7 +153,9 @@ def main():
assert dist.get_world_size() == 4
nccl = PyNcclCommunicator(dist.group.WORLD, rank)
assert not nccl.disabled
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = (
args.reduced_precision
)
report = {"cases": [], "paired_max_row_l2_limit": 0.001}
passed = True
try:
@@ -288,7 +294,12 @@ def main():
ch, cb, ci = control(
hidden=ch, injection=ci, partial=(noise + cb * 0.025).half()
)
sh, sb, si = candidate(
drift_candidate = (
fp32_candidate
if args.drift_communication == "fp32"
else candidate
)
sh, sb, si = drift_candidate(
hidden=sh, injection=si, partial=(noise + sb * 0.025).half()
)
rh, rb, ri = fp32_reference(
@@ -194,13 +194,16 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--peer", action="store_true")
parser.add_argument("--reduced-precision", action="store_true")
parser.add_argument("--tokens", type=int, nargs="+", default=[4096, 7344, 11424])
args = parser.parse_args()
rank = int(os.environ["LOCAL_RANK"])
torch.accelerator.set_device_index(rank)
torch.set_grad_enabled(False)
dist.init_process_group("gloo")
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = (
args.reduced_precision
)
group = Group(rank)
pp = NS(is_first_rank=True, is_last_rank=True)
cfg = NS(
@@ -10,6 +10,69 @@ from vllm.models.qwen4_exp.nvidia import sm70_hc_prefill_peer as peer
from vllm.runtime_resources import release_runtime_resources, runtime_resources_for
@pytest.mark.parametrize("reduced", [False, True])
@pytest.mark.parametrize(
"rows,accepted",
[(4095, False), (4096, True), (4097, True), (16384, True), (16385, False)],
)
def test_prefill_admission_preserves_mtp_precision_policy(
monkeypatch, reduced, rows, accepted
):
from vllm.config import CUDAGraphMode
from vllm.models.qwen4_exp.nvidia import model
monkeypatch.setattr(
torch.backends.cuda.matmul, "allow_fp16_reduced_precision_reduction", reduced
)
monkeypatch.setattr(torch.backends.cuda.matmul, "allow_fp16_accumulation", False)
monkeypatch.setattr(model, "is_forward_context_available", lambda: True)
context = NS(cudagraph_runtime_mode=CUDAGraphMode.NONE)
monkeypatch.setattr(model, "get_forward_context", lambda: context)
owner = NS(_hc_prefill_sequence_qualified=True)
inputs = torch.empty(rows, dtype=torch.long)
selected = model.Qwen4ExpForCausalLM._use_hc_prefill_sequence_rows(
owner, inputs, None, None, {}
)
assert selected is accepted
assert torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction is reduced
context.cudagraph_runtime_mode = CUDAGraphMode.FULL
assert not model.Qwen4ExpForCausalLM._use_hc_prefill_sequence_rows(
owner, inputs, None, None, {}
)
context.cudagraph_runtime_mode = CUDAGraphMode.NONE
monkeypatch.setattr(torch.backends.cuda.matmul, "allow_fp16_accumulation", True)
assert not model.Qwen4ExpForCausalLM._use_hc_prefill_sequence_rows(
owner, inputs, None, None, {}
)
def test_single_attention_partial_uses_fp32_transport(monkeypatch):
from vllm import distributed
from vllm.models.qwen4_exp.nvidia.ops import hc
seen = []
def reduce_scatter(value, dim):
seen.append(value)
assert value.dtype == torch.float32 and dim == 0
return value[:1024]
monkeypatch.setattr(
distributed, "get_tp_group", lambda: NS(reduce_scatter=reduce_scatter)
)
monkeypatch.setattr(peer, "current_runtime_resources", lambda: {})
monkeypatch.setattr(
hc, "hc_combine_norm", lambda hidden, payload, *args: (payload, hidden)
)
payload = torch.arange(4096 * 4).reshape(4096, 4).half()
hidden = torch.zeros(1024, 16, dtype=torch.half)
actual, _ = peer._combine_norm(
hidden, payload, torch.zeros(1024, 4), torch.zeros(16), 1e-6, False, True, True
)
assert len(seen) == 1 and torch.equal(seen[0], payload.float())
assert actual.dtype == payload.dtype and torch.equal(actual, payload[:1024])
@pytest.mark.parametrize("separate", [False, True])
def test_peer_fallback_keeps_unrounded_moe_sum(monkeypatch, separate):
from vllm import distributed
+4 -1
View File
@@ -1359,6 +1359,10 @@ class Qwen4ExpForCausalLM(
input_ids, inputs_embeds, intermediate_tensors, kwargs
)
if sequence_parallel:
logger.info_once(
"Using qualified TP4 HC prefill row owners with FP32 partial "
"communication; decode keeps its existing GEMM policy."
)
if self._hc_prefill_sequence_model is None:
config = copy.copy(self.vllm_config)
config.compilation_config = copy.copy(config.compilation_config)
@@ -1398,7 +1402,6 @@ class Qwen4ExpForCausalLM(
and intermediate_tensors is None
and kwargs.get("deepstack_input_embeds") is None
and (inputs_embeds is None or inputs_embeds.dtype == torch.float16)
and not torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction
and not torch.backends.cuda.matmul.allow_fp16_accumulation
and is_forward_context_available()
and get_forward_context().cudagraph_runtime_mode == CUDAGraphMode.NONE
@@ -313,7 +313,9 @@ def _combine_norm(
hidden, first, second, injection, weight, eps, normalize
)
if partial:
source = first if second is None else first.float() + second.float()
# Attention can supply one partial, while MoE supplies two. Both
# use the qualified FP32 transport before the original FP16 combine.
source = first.float() if second is None else first.float() + second.float()
reduced = group.reduce_scatter(source, dim=0).to(first.dtype)
else:
reduced = first.reshape(4, -1, first.shape[-1])[group.rank_in_group]