Skip to content

Commit 4aec4ea

Browse files
committed
[KMCompiler][Benchmark][Test] Fix the fp8e4nv gate, memory guard and vendor op discovery for fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert
Three issues in this operator's test and benchmark, each of which makes the suite report something untrue. 1. is_support_fp8e4nv() gated on get_device_capability() >= (8, 9). That threshold means "Ada or newer" on NVIDIA only; other vendors report their own major/minor on a different scale, so it is a false negative that skips the whole file on hardware that supports the dtype. MetaX C550 reports (8, 0) and converts fp8e4nv bit-identically to torch. Added supports_fp8e4nv() to flag_gems/utils/device_info.py, which keeps the NVIDIA rule where it means something and lets other vendors opt in explicitly. Four other files carry the same local copy of this check and are left alone here. 2. The memory guard excluded shapes via a list hardcoded for an 80GB H800 -- which still OOMs on 64GB cards -- and skipped them with a bare return, so they were counted as passed. Replaced with a budget measured against free device memory, skipping explicitly. It collects before measuring and counts the allocator's cached blocks as available: releasing them with empty_cache() instead makes a marginal allocation fail that otherwise succeeds. 3. The benchmark probed torch.ops._C for a compiled reference without importing the library that registers it, and torch.ops._C gives no hint that nothing did. Where the reference IS installed the benchmark reported it missing and skipped, so no comparison ran at all. The provider is not always vLLM's own: on MetaX it is mcoplib._C, while the vllm wheel there fails to load. Added benchmark/vendor_ops.py to try each in turn and log why one is rejected.
1 parent a841b2e commit 4aec4ea

4 files changed

Lines changed: 287 additions & 35 deletions

File tree

benchmark/test_fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,16 @@
1919
import torch
2020

2121
import flag_gems
22-
from flag_gems.utils.device_info import get_device_capability
22+
from flag_gems.utils.device_info import supports_fp8e4nv as is_support_fp8e4nv
2323

2424
from . import base
25+
from .vendor_ops import VENDOR_OP_LIBS, load_vendor_op
2526

2627

27-
def is_support_fp8e4nv():
28-
major, minor = get_device_capability()
29-
return major * 10 + minor >= 89
30-
31-
32-
VLLM_REF_AVAILABLE = hasattr(
33-
torch.ops._C, "fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert"
34-
)
28+
# The reference is whichever compiled library the platform ships, not
29+
# necessarily vLLM's own; see benchmark/vendor_ops.py.
30+
_VENDOR_REF = load_vendor_op("fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert")
31+
VLLM_REF_AVAILABLE = _VENDOR_REF is not None
3532
HEAD_DIM = 512
3633
ROPE_DIM = 64
3734
HEAD_BYTES = 584
@@ -59,7 +56,7 @@ class FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark(base.Benchmark):
5956
def __init__(self):
6057
super().__init__(
6158
"fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert",
62-
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert,
59+
_VENDOR_REF,
6360
[torch.bfloat16],
6461
)
6562
self.set_gems(flag_gems.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert)
@@ -170,10 +167,12 @@ def make_input(param: TestParam):
170167

171168
@pytest.mark.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert
172169
@pytest.mark.skipif(
173-
not VLLM_REF_AVAILABLE, reason="The referenced vLLM implementation is not installed"
170+
not VLLM_REF_AVAILABLE,
171+
reason="No vendor kernel found for this operator (tried %s)"
172+
% ", ".join(VENDOR_OP_LIBS),
174173
)
175174
@pytest.mark.skipif(
176-
not is_support_fp8e4nv(), reason="Do not support fp8e4nv when capability < 89"
175+
not is_support_fp8e4nv(), reason="Device does not support fp8e4nv"
177176
)
178177
def test_fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert():
179178
bench = FusedDeepseekV4QnormRopeKVRopeQuantInsertBenchmark()

benchmark/vendor_ops.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Locate vendor-provided reference kernels under `torch.ops._C`.
15+
16+
Several benchmarks compare against a compiled reference registered in the `_C`
17+
namespace and probe for it with `hasattr(torch.ops._C, name)`. That probe only
18+
works if something already imported the library that registers the op, and
19+
`torch.ops._C` gives no hint that nothing did -- so on a platform where the
20+
reference IS installed the benchmark silently reports it as missing and either
21+
skips or falls back to an eager-PyTorch baseline, producing speedup numbers
22+
against the wrong thing.
23+
24+
The registering library is not always vLLM's own. On MetaX the provider is
25+
`mcoplib._C`, while the `vllm` wheel there is a stock CUDA build whose `_C`
26+
fails to load at all. Importing only `vllm._C` therefore finds nothing.
27+
28+
Note `import vllm` / `import mcoplib` alone is not enough: the compiled
29+
submodule has to be imported before the schemas register.
30+
"""
31+
32+
import importlib
33+
import logging
34+
35+
import torch
36+
37+
logger = logging.getLogger(__name__)
38+
39+
# Tried in order; the first one providing the operator wins.
40+
VENDOR_OP_LIBS = ("vllm._C", "mcoplib._C")
41+
42+
43+
def load_vendor_op(op_name, libs=VENDOR_OP_LIBS):
44+
"""Return `torch.ops._C.<op_name>`, importing vendor libraries as needed.
45+
46+
Returns None if no library provides it. Import failures are logged rather
47+
than swallowed -- a silent `except: pass` here is what makes a missing
48+
baseline indistinguishable from an unimportable one.
49+
"""
50+
fn = getattr(torch.ops._C, op_name, None)
51+
if callable(fn):
52+
return fn
53+
54+
for lib in libs:
55+
try:
56+
importlib.import_module(lib)
57+
except Exception as e:
58+
logger.info("vendor op library %s unavailable: %s", lib, e)
59+
continue
60+
fn = getattr(torch.ops._C, op_name, None)
61+
if callable(fn):
62+
logger.info("found %s in %s", op_name, lib)
63+
return fn
64+
logger.info("%s loaded but does not provide %s", lib, op_name)
65+
66+
logger.info(
67+
"no vendor kernel for %s (tried %s)", op_name, ", ".join(libs)
68+
)
69+
return None

src/flag_gems/utils/device_info.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
from dataclasses import dataclass
1717
from functools import lru_cache
1818

19+
import torch
20+
21+
from flag_gems.runtime import device as _runtime_device
1922
from flag_gems.runtime import torch_device_fn
2023

2124

@@ -69,6 +72,36 @@ def get_device_capability() -> tuple[int, int]:
6972
return (0, 0)
7073

7174

75+
# Vendors whose hardware does FP8 E4M3 but whose reported compute capability is
76+
# below the NVIDIA sm_89 threshold, so the capability rule would wrongly exclude
77+
# them. Add a vendor here only after verifying on the hardware that a Triton
78+
# `tl.float8e4nv` conversion matches `torch.float8_e4m3fn` bit-for-bit.
79+
# metax: C550 reports capability (8, 0) and converts bit-for-bit identically
80+
# to torch (verified 2026-08-05 on MetaX C550).
81+
_FP8E4NV_CAPABLE_VENDORS = frozenset({"metax"})
82+
83+
84+
@lru_cache(maxsize=1)
85+
def supports_fp8e4nv() -> bool:
86+
"""Whether this device supports FP8 E4M3 (`tl.float8e4nv`).
87+
88+
NVIDIA gates E4M3 on sm_89+, and tests have historically spelled that check
89+
as `get_device_capability() >= (8, 9)`. That number only means "Ada or
90+
newer" on NVIDIA -- other vendors report their own major/minor on a
91+
different scale, so applying the threshold to them is a false negative that
92+
silently skips whole test files on hardware that supports the dtype.
93+
94+
Keep the capability rule where it is meaningful, and consult an explicit
95+
per-vendor list elsewhere.
96+
"""
97+
if not hasattr(torch, "float8_e4m3fn"):
98+
return False
99+
if _runtime_device.vendor_name in _FP8E4NV_CAPABLE_VENDORS:
100+
return True
101+
major, minor = get_device_capability()
102+
return major * 10 + minor >= 89
103+
104+
72105
@lru_cache(maxsize=1)
73106
def get_device_info() -> DeviceInfo:
74107
props = get_device_properties()

0 commit comments

Comments
 (0)