Signed-off-by: GUOGUO <xwdong1998@163.com> Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
136 lines
5.3 KiB
Python
136 lines
5.3 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
|
"""Direct tests for the synchronous launch-failure fallback (C3a).
|
|
|
|
Simulates a backend-specific Triton launch failure (the launcher raises
|
|
synchronously) and verifies the two-layer response: the failed config is
|
|
recorded, the caller gets the correct native result, the same config is not
|
|
retried, and a different constexpr variant stays eligible.
|
|
|
|
The failed-key set is module-global; the autouse fixture snapshots and
|
|
restores it so tests are self-contained and independent of execution order.
|
|
Collection is safe on non-Triton environments: the Triton-only kernel symbol
|
|
is resolved lazily inside each test and skips if unavailable.
|
|
"""
|
|
|
|
import pytest
|
|
import torch
|
|
|
|
from vllm_omni.diffusion.layers import adalayernorm as adaln_mod
|
|
from vllm_omni.diffusion.layers.adalayernorm import (
|
|
_FAILED_ADALN_KEYS,
|
|
AdaLayerNorm,
|
|
)
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cuda]
|
|
|
|
# Unique channels + eps so the failed keys used here cannot collide with
|
|
# other tests in the same session.
|
|
_HIDDEN = 2048
|
|
_EPS = 1e-5
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_failed_keys():
|
|
saved = set(_FAILED_ADALN_KEYS)
|
|
_FAILED_ADALN_KEYS.clear()
|
|
try:
|
|
yield
|
|
finally:
|
|
_FAILED_ADALN_KEYS.clear()
|
|
_FAILED_ADALN_KEYS.update(saved)
|
|
|
|
|
|
def _make(bs=1):
|
|
m = AdaLayerNorm(_HIDDEN, eps=_EPS).to(device="cuda", dtype=torch.bfloat16)
|
|
g = torch.Generator(device="cuda").manual_seed(31)
|
|
x = torch.randn(bs, 512, _HIDDEN, generator=g, device="cuda", dtype=torch.bfloat16)
|
|
scale = torch.randn(1, _HIDDEN, generator=g, device="cuda", dtype=torch.bfloat16)
|
|
shift = torch.randn(1, _HIDDEN, generator=g, device="cuda", dtype=torch.bfloat16)
|
|
return m, x, scale, shift
|
|
|
|
|
|
def _get_kernel():
|
|
kernel = getattr(adaln_mod, "_adaln_scale_shift_layernorm_kernel", None)
|
|
if kernel is None:
|
|
pytest.skip("Triton AdaLayerNorm kernel unavailable")
|
|
return kernel
|
|
|
|
|
|
def test_shared_launch_failure_fallback_and_cache(monkeypatch):
|
|
kernel = _get_kernel()
|
|
m, x, scale, shift = _make(1)
|
|
native = m.forward_native(x, scale, shift)
|
|
|
|
calls = {"n": 0}
|
|
|
|
def raising_run(self, *args, **kwargs):
|
|
calls["n"] += 1
|
|
raise RuntimeError("simulated synchronous launch failure")
|
|
|
|
monkeypatch.setattr(type(kernel), "run", raising_run)
|
|
|
|
# Phase 1: the launch raises synchronously; the caller must receive the
|
|
# correct native result and the failed key must be recorded.
|
|
out = m.forward_cuda(x, scale, shift)
|
|
torch.testing.assert_close(out.float(), native.float())
|
|
expected_key = (x.device.index, _HIDDEN, x.dtype, False, False, False, False)
|
|
assert expected_key in _FAILED_ADALN_KEYS
|
|
assert calls["n"] == 1 # the fused launcher was attempted exactly once
|
|
|
|
# Phase 2: the same config must NOT retry the launcher (failed-key
|
|
# cache) and still return the correct native result.
|
|
calls["n"] = 0
|
|
out2 = m.forward_cuda(x, scale, shift)
|
|
assert calls["n"] == 0
|
|
torch.testing.assert_close(out2.float(), native.float())
|
|
|
|
|
|
def test_shared_failure_then_per_sample_variant_eligible(monkeypatch):
|
|
# The failed-key cache is per constexpr variant: after the shared
|
|
# (False, False) variant fails, the per-sample (True, True) variant must
|
|
# still attempt the launcher and succeed.
|
|
kernel = _get_kernel()
|
|
m, x, shared_scale, shared_shift = _make(2)
|
|
shared_native = m.forward_native(x, shared_scale, shared_shift)
|
|
shared_key = (x.device.index, _HIDDEN, x.dtype, False, False, False, False)
|
|
per_sample_key = (x.device.index, _HIDDEN, x.dtype, False, False, True, True)
|
|
calls = {"n": 0}
|
|
|
|
def raising_run(self, *args, **kwargs):
|
|
calls["n"] += 1
|
|
raise RuntimeError("simulated synchronous shared launch failure")
|
|
|
|
# Establish the shared failure in this test; the autouse fixture clears
|
|
# failures from other tests, so their cache entries cannot prove isolation.
|
|
with monkeypatch.context() as shared_patch:
|
|
shared_patch.setattr(type(kernel), "run", raising_run)
|
|
shared_out = m.forward_cuda(x, shared_scale, shared_shift)
|
|
torch.testing.assert_close(shared_out.float(), shared_native.float())
|
|
assert calls["n"] == 1
|
|
assert shared_key in _FAILED_ADALN_KEYS
|
|
assert per_sample_key not in _FAILED_ADALN_KEYS
|
|
|
|
m.forward_cuda(x, shared_scale, shared_shift)
|
|
assert calls["n"] == 1, "the cached shared failure must not retry"
|
|
|
|
g = torch.Generator(device="cuda").manual_seed(37)
|
|
scale = torch.randn(2, 1, _HIDDEN, generator=g, device="cuda", dtype=torch.bfloat16)
|
|
shift = torch.randn(2, 1, _HIDDEN, generator=g, device="cuda", dtype=torch.bfloat16)
|
|
calls["n"] = 0
|
|
|
|
def counting_run(self, *args, **kwargs):
|
|
calls["n"] += 1
|
|
return real_run(self, *args, **kwargs)
|
|
|
|
real_run = type(kernel).run
|
|
monkeypatch.setattr(type(kernel), "run", counting_run)
|
|
|
|
out = adaln_mod._adaln_fused_forward(m, x, scale, shift)
|
|
assert out is not None, "the per-sample variant must remain eligible after a shared failure"
|
|
native = m.forward_native(x, scale, shift)
|
|
torch.testing.assert_close(out.float(), native.float(), atol=2e-2, rtol=2e-2)
|
|
assert calls["n"] == 1
|
|
assert shared_key in _FAILED_ADALN_KEYS
|
|
assert per_sample_key not in _FAILED_ADALN_KEYS
|