mirror of
https://github.com/pytorch/pytorch.git
synced 2026-10-28 18:56:51 +08:00
This reverts commit e506c0307c.
Reverted https://github.com/pytorch/pytorch/pull/200800 on behalf of https://github.com/meta-codesync due to Diff reverted internally ([comment](https://github.com/pytorch/pytorch/pull/200800#issuecomment-6459836373))
526 lines
22 KiB
Python
526 lines
22 KiB
Python
# Owner(s): ["module: dsl-native-ops"]
|
|
"""End-to-end tests for native-AOT kernels on aten::topk @ CUDA.
|
|
|
|
Three-layer routing under test (declaration: torch/_native/ops/topk/aot.py):
|
|
|
|
* covered calls -> the AOT kernel in the structured wrapper, because the JIT
|
|
layer's conds subtract AOT coverage
|
|
* JIT-only exact-N register calls and covered calls without AOT -> the JIT override
|
|
* everything else -> stock aten
|
|
|
|
Layers are isolated with the process-level switches TORCH_DISABLE_NATIVE_JIT and
|
|
TORCH_DISABLE_NATIVE_AOT in subprocesses: with the JIT layer off, a top-k DSL kernel
|
|
in a profile can only come from the AOT hook. Values are checked against a sort-based
|
|
reference, which topk routing cannot affect.
|
|
|
|
Tests needing the AOT kernels skip unless this build embedded them; the
|
|
correctness tests run everywhere, since covered calls must be correct through stock
|
|
aten when the artifacts are absent.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
|
|
import torch
|
|
from torch.testing._internal.common_cuda import TEST_CUDA
|
|
from torch.testing._internal.common_utils import (
|
|
run_tests,
|
|
skipIfNoCuteDSL,
|
|
skipIfNoNativeAot,
|
|
TestCase,
|
|
)
|
|
|
|
|
|
def skipIfNoJitTopk(fn):
|
|
"""Both top-k routes require Hopper or newer."""
|
|
capability = (
|
|
torch.cuda.get_device_capability() if torch.cuda.is_available() else (0, 0)
|
|
)
|
|
return unittest.skipUnless(capability[0] >= 9, "JIT topk override needs sm_90+")(fn)
|
|
|
|
|
|
# Representative points in the original measured grid.
|
|
GRID_N = (2048, 4096, 8192, 16384)
|
|
GRID_K = (64, 128, 256)
|
|
# Enough rows to pass the full-wave perf gate on any current GPU.
|
|
M = 256
|
|
JIT_ONLY_REGISTER = ((16, 2048), (32, 256))
|
|
|
|
# Subprocess probe with the JIT layer disabled and the AOT hooks live. Both layers
|
|
# launch the same CuTeDSL kernel, so the name in the profile says a DSL kernel ran
|
|
# while the environment is what makes it the AOT one; aten shows mbtopk instead.
|
|
_PROBE = r"""
|
|
import json, torch
|
|
from torch.profiler import profile, ProfilerActivity
|
|
|
|
results = []
|
|
for case in json.loads({cases!r}):
|
|
dtype = getattr(torch, case["dtype"])
|
|
kwargs = case.get("kwargs", {{}})
|
|
torch.manual_seed(case["n"] * 31 + case["k"])
|
|
x = torch.randn({m}, case["n"], device="cuda", dtype=dtype)
|
|
if case.get("det"):
|
|
torch.use_deterministic_algorithms(True)
|
|
out = None
|
|
if case.get("out_variant"):
|
|
out = (
|
|
torch.empty({m}, case["k"], device="cuda", dtype=dtype),
|
|
torch.empty({m}, case["k"], device="cuda", dtype=torch.int64),
|
|
)
|
|
kwargs = dict(kwargs, out=out)
|
|
with profile(activities=[ProfilerActivity.CUDA]) as prof:
|
|
v, i = torch.topk(x, case["k"], dim=-1, **kwargs)
|
|
torch.cuda.synchronize()
|
|
ran_dsl = any(
|
|
"RadixSelectTopK" in e.name or "RegisterTopK" in e.name
|
|
for e in prof.events()
|
|
if e.device_type.name == "CUDA"
|
|
)
|
|
ref_v = torch.sort(x, dim=-1, descending=True).values[..., : case["k"]]
|
|
values_ok = bool(torch.equal(v, ref_v))
|
|
gather_ok = bool(torch.equal(x.gather(-1, i), v))
|
|
if case.get("det"):
|
|
torch.use_deterministic_algorithms(False)
|
|
results.append(
|
|
{{"ran_dsl": ran_dsl, "values_ok": values_ok, "gather_ok": gather_ok,
|
|
"index_dtype": str(i.dtype)}}
|
|
)
|
|
print("PROBE_RESULTS=" + json.dumps(results))
|
|
"""
|
|
|
|
|
|
def _runs_dsl(x, k=64) -> bool:
|
|
"""Whether topk(x, k) launched a DSL kernel, in this process.
|
|
|
|
Which layer served it is decided by the caller's environment: both launch the
|
|
same kernel, so the name in the profile only says that one of them did."""
|
|
from torch.profiler import profile, ProfilerActivity
|
|
|
|
with profile(activities=[ProfilerActivity.CUDA]) as prof:
|
|
torch.topk(x, k, dim=-1)
|
|
torch.cuda.synchronize()
|
|
return any(
|
|
"RadixSelectTopK" in e.name or "RegisterTopK" in e.name
|
|
for e in prof.events()
|
|
if e.device_type.name == "CUDA"
|
|
)
|
|
|
|
|
|
def _run_probe(cases, extra_env):
|
|
env = dict(os.environ, **extra_env)
|
|
src = _PROBE.format(cases=json.dumps(cases), m=M)
|
|
proc = subprocess.run(
|
|
[sys.executable, "-c", src],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
timeout=1200,
|
|
)
|
|
if proc.returncode != 0:
|
|
raise RuntimeError(proc.stderr[-3000:])
|
|
line = next(
|
|
line for line in proc.stdout.splitlines() if line.startswith("PROBE_RESULTS=")
|
|
)
|
|
return json.loads(line[len("PROBE_RESULTS=") :])
|
|
|
|
|
|
class TestNativeAotTopKDeclaration(TestCase):
|
|
def test_archs_are_canonical_portable_targets(self):
|
|
from torch._native.ops.topk import aot
|
|
|
|
# Top-k's tuning tables cover compute capability majors 9 and 10.
|
|
# Native AOT uses one widest portable compiler target per major:
|
|
# sm_90 is the available portable SM9x target, while
|
|
# the same-major compatibility rule makes sm_100f cover known SM10x devices.
|
|
self.assertEqual(aot.ARCHS, ("sm_90", "sm_100f"))
|
|
|
|
def test_dispatch_alignment_is_radix_only(self):
|
|
from torch._native.ops.topk import aot
|
|
|
|
prelude = aot.cpp_dispatch_prelude()
|
|
self.assertNotIn("N % 4", prelude)
|
|
self.assertNotIn("getCurrentDeviceProperties", prelude)
|
|
self.assertIn("_naot_props->major", prelude)
|
|
self.assertIn("_naot_props->multiProcessorCount", prelude)
|
|
|
|
register = aot.cpp_dispatch(
|
|
{
|
|
"kernel": "register",
|
|
"dtype": "float32",
|
|
"K": 16,
|
|
"N_rung": "64_128_256_512_1024",
|
|
}
|
|
)
|
|
radix = aot.cpp_dispatch(
|
|
{
|
|
"kernel": "radix",
|
|
"dtype": "float32",
|
|
"K": 64,
|
|
"deterministic": False,
|
|
"fixed_vec_iters": None,
|
|
}
|
|
)
|
|
self.assertNotIn("N % 4", register)
|
|
self.assertIn("cc_major < 10 || N != 1024", register)
|
|
self.assertIn("N % 4 == 0", radix)
|
|
|
|
|
|
@unittest.skipUnless(TEST_CUDA, "CUDA required")
|
|
@skipIfNoCuteDSL
|
|
class TestNativeAotTopK(TestCase):
|
|
@skipIfNoNativeAot("topk")
|
|
def test_covered_grid_routes_to_aot(self):
|
|
cases = [
|
|
{"dtype": dtype, "n": n, "k": k}
|
|
for dtype in ("float32", "bfloat16")
|
|
for n in GRID_N
|
|
for k in GRID_K
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_JIT": "1"})
|
|
for case, r in zip(cases, results):
|
|
self.assertTrue(r["ran_dsl"], f"AOT kernel did not fire for {case}")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
self.assertEqual(r["index_dtype"], "torch.int64")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_register_grid_routing(self):
|
|
cases = [
|
|
{"dtype": "float32", "n": n, "k": 16} for n in (64, 128, 256, 512, 1024)
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_JIT": "1"})
|
|
major = torch.cuda.get_device_capability()[0]
|
|
for case, r in zip(cases, results):
|
|
self.assertEqual(
|
|
r["ran_dsl"],
|
|
not (major >= 10 and case["n"] == 1024),
|
|
f"unexpected AOT register routing for {case}",
|
|
)
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
self.assertEqual(r["index_dtype"], "torch.int64")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_dynamic_n_and_work_rungs_route_to_aot(self):
|
|
shapes = (
|
|
(64, 3072),
|
|
(64, 4100),
|
|
(64, 4356),
|
|
(64, 4612),
|
|
(64, 4868),
|
|
(512, 4096),
|
|
(512, 5120),
|
|
(512, 6140),
|
|
(512, 6144),
|
|
(1024, 32772),
|
|
(1024, 34820),
|
|
(1024, 36860),
|
|
)
|
|
cases = [
|
|
{"dtype": dtype, "n": n, "k": k, "det": deterministic}
|
|
for dtype in ("float32", "bfloat16")
|
|
for deterministic in (False, True)
|
|
for k, n in shapes
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_JIT": "1"})
|
|
for case, r in zip(cases, results):
|
|
self.assertTrue(r["ran_dsl"], f"AOT kernel did not fire for {case}")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_deterministic_mode_routes_to_aot_bit_exact(self):
|
|
# Det mode is on the grid, so its kernel must fire and match aten bit-exactly.
|
|
# Probe values are torch.randn; ties are exercised in the next test.
|
|
cases = [
|
|
{"dtype": "float32", "n": 4096, "k": 64, "det": True},
|
|
{"dtype": "bfloat16", "n": 2048, "k": 128, "det": True},
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_JIT": "1"})
|
|
for case, r in zip(cases, results):
|
|
self.assertTrue(r["ran_dsl"], f"AOT det kernel did not fire for {case}")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_deterministic_ties_bit_exact(self):
|
|
# Tie-heavy input, so det-mode indices must match aten's exactly. The
|
|
# reference runs under disabled(), or it would come from the route under test.
|
|
prior = torch.are_deterministic_algorithms_enabled()
|
|
try:
|
|
torch.use_deterministic_algorithms(True)
|
|
for dtype in (torch.float32, torch.bfloat16):
|
|
torch.manual_seed(11)
|
|
x = torch.randint(0, 50, (M * 8, 4096), device="cuda").to(dtype)
|
|
v, i = torch.topk(x, 64)
|
|
with torch.backends.python_native.cutedsl.disabled():
|
|
rv, ri = torch.topk(x, 64)
|
|
self.assertTrue(torch.equal(v, rv), f"values differ ({dtype})")
|
|
self.assertTrue(torch.equal(i, ri), f"indices differ ({dtype})")
|
|
finally:
|
|
torch.use_deterministic_algorithms(prior)
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_out_variant_routes_to_aot(self):
|
|
results = _run_probe(
|
|
[{"dtype": "float32", "n": 4096, "k": 64, "out_variant": True}],
|
|
{"TORCH_DISABLE_NATIVE_JIT": "1"},
|
|
)
|
|
self.assertTrue(results[0]["ran_dsl"])
|
|
self.assertTrue(results[0]["values_ok"])
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_uncovered_calls_avoid_aot(self):
|
|
cases = [
|
|
*({"dtype": "float32", "n": n, "k": k} for k, n in JIT_ONLY_REGISTER),
|
|
{"dtype": "float32", "n": 124, "k": 64}, # below every radix range
|
|
{"dtype": "float32", "n": 4101, "k": 64}, # not vector-aligned
|
|
{"dtype": "float32", "n": 4096, "k": 100}, # off-grid K
|
|
{"dtype": "float32", "n": 512, "k": 32}, # off-grid register N
|
|
{"dtype": "bfloat16", "n": 256, "k": 16}, # register is fp32-only
|
|
{"dtype": "float16", "n": 4096, "k": 64},
|
|
{"dtype": "float64", "n": 4096, "k": 64},
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_JIT": "1"})
|
|
for case, r in zip(cases, results):
|
|
self.assertFalse(r["ran_dsl"], f"{case} must not route to AOT")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
@skipIfNoJitTopk
|
|
def test_aot_coverage_is_subset_of_jit_eligibility(self):
|
|
from torch._native import aot_manifest
|
|
from torch._native.ops.topk import cutedsl_impl
|
|
from torch._native.ops.topk.aot import _radix_min_n
|
|
|
|
major = torch.cuda.get_device_capability()[0]
|
|
cases = [
|
|
(torch.float32, 16, 64),
|
|
(torch.float32, 16, 2048),
|
|
(torch.float32, 32, 256),
|
|
(torch.float32, 32, 512),
|
|
(torch.bfloat16, 16, 256),
|
|
]
|
|
for dtype_name, dtype in (
|
|
("float32", torch.float32),
|
|
("bfloat16", torch.bfloat16),
|
|
):
|
|
for k in (64, 128, 256, 512, 1024):
|
|
min_n = _radix_min_n(dtype_name, k, major)
|
|
cases.extend(
|
|
(
|
|
(dtype, k, min_n - 4),
|
|
(dtype, k, min_n),
|
|
(dtype, k, min_n + 4),
|
|
)
|
|
)
|
|
for dtype, k, n in cases:
|
|
with self.subTest(dtype=dtype, k=k, n=n):
|
|
x = torch.empty(M, n, dtype=dtype, device="cuda")
|
|
jit_eligible = cutedsl_impl._eligible(x, k, -1, True, True)
|
|
aot_covered = aot_manifest.covers("topk", "CUDA", (x, k), {})
|
|
if dtype == torch.float32 and (k, n) in JIT_ONLY_REGISTER:
|
|
self.assertTrue(jit_eligible)
|
|
self.assertFalse(aot_covered)
|
|
else:
|
|
self.assertEqual(aot_covered, jit_eligible)
|
|
|
|
@skipIfNoJitTopk
|
|
def test_exact_n_register_cases_route_to_jit(self):
|
|
cases = [{"dtype": "float32", "n": n, "k": k} for k, n in JIT_ONLY_REGISTER]
|
|
results = _run_probe(cases, {})
|
|
for case, r in zip(cases, results):
|
|
self.assertTrue(r["ran_dsl"], f"JIT kernel did not fire for {case}")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
|
|
@skipIfNoJitTopk
|
|
def test_shared_policy_cases_route_to_jit_without_aot(self):
|
|
cases = [
|
|
*({"dtype": "float32", "n": n, "k": 16} for n in (64, 128, 256, 512, 1024)),
|
|
{"dtype": "float32", "n": 4100, "k": 64},
|
|
{"dtype": "float32", "n": 5120, "k": 512, "det": True},
|
|
{"dtype": "bfloat16", "n": 2048, "k": 64},
|
|
{
|
|
"dtype": "bfloat16",
|
|
"n": 2048,
|
|
"k": 64,
|
|
"out_variant": True,
|
|
},
|
|
]
|
|
results = _run_probe(cases, {"TORCH_DISABLE_NATIVE_AOT": "1"})
|
|
for case, r in zip(cases, results):
|
|
self.assertTrue(r["ran_dsl"], f"JIT kernel did not fire for {case}")
|
|
self.assertTrue(r["values_ok"], f"values mismatch for {case}")
|
|
self.assertTrue(r["gather_ok"], f"gather mismatch for {case}")
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_disabled_context_masks_aot_in_process(self):
|
|
# cutedsl.disabled() flips the native-AOT Context switch as well as the JIT
|
|
# layer, so no DSL kernel may run inside the block.
|
|
from torch import _native
|
|
|
|
x = torch.randn(M, 4096, device="cuda")
|
|
pn = torch.backends.python_native
|
|
self.assertTrue(_native.aot_enabled())
|
|
with pn.cutedsl.disabled():
|
|
self.assertFalse(_native.aot_enabled())
|
|
self.assertFalse(_runs_dsl(x))
|
|
self.assertTrue(_native.aot_enabled())
|
|
self.assertTrue(_runs_dsl(x))
|
|
|
|
def test_covered_call_correct_regardless_of_routing(self):
|
|
# Correct whichever layer serves it, including stock aten with no AOT lib.
|
|
torch.manual_seed(2)
|
|
x = torch.randn(M, 4096, device="cuda")
|
|
v, i = torch.topk(x, 64, dim=-1)
|
|
ref_v = torch.sort(x, dim=-1, descending=True).values[..., :64]
|
|
self.assertEqual(v, ref_v, atol=0, rtol=0)
|
|
self.assertEqual(x.gather(-1, i), v, atol=0, rtol=0)
|
|
|
|
def test_covered_call_correct_without_aot_lib(self):
|
|
# Null-hook degradation, forced: both layers off in a subprocess.
|
|
results = _run_probe(
|
|
[{"dtype": "float32", "n": 4096, "k": 64}],
|
|
{"TORCH_DISABLE_NATIVE_JIT": "1", "TORCH_DISABLE_NATIVE_AOT": "1"},
|
|
)
|
|
self.assertFalse(results[0]["ran_dsl"])
|
|
self.assertTrue(results[0]["values_ok"])
|
|
|
|
def test_covered_axes_function_directly(self):
|
|
# covered_axes() is plain Python, loaded by file path (through the loader the
|
|
# declaration tooling itself uses) since the module is stdlib-only at import.
|
|
import os
|
|
|
|
from torchgen.native_aot_decl import load_by_path
|
|
|
|
path = os.path.join(
|
|
os.path.dirname(torch.__file__), "_native", "ops", "topk", "aot.py"
|
|
)
|
|
mod = load_by_path("topk_aot_t", path)
|
|
|
|
x = torch.empty(M, 4100, device="cuda")
|
|
v = mod.covered_axes(x, 64)
|
|
self.assertEqual(v["kernel"], "radix")
|
|
self.assertEqual(v["N"], None)
|
|
self.assertEqual(v["K"], 64)
|
|
self.assertEqual(v["dtype"], torch.float32)
|
|
self.assertEqual(v["scalar_tail_iters"], None)
|
|
self.assertEqual(v["fixed_vec_iters"], None)
|
|
self.assertTrue(v["eligible"])
|
|
register = mod.covered_axes(torch.empty(M, 256, device="cuda"), 16)
|
|
self.assertEqual(register["kernel"], "register")
|
|
self.assertEqual(register["N"], None)
|
|
self.assertEqual(register["N_rung"], "64_128_256_512_1024")
|
|
self.assertTrue(register["eligible"])
|
|
register_1024 = mod.covered_axes(torch.empty(M, 1024, device="cuda"), 16)
|
|
if torch.cuda.get_device_capability()[0] >= 10:
|
|
self.assertIsNone(register_1024["N_rung"])
|
|
self.assertFalse(register_1024["eligible"])
|
|
else:
|
|
self.assertEqual(register_1024["N_rung"], "64_128_256_512_1024")
|
|
self.assertTrue(register_1024["eligible"])
|
|
base = torch.empty(M, 4096, device="cuda")
|
|
cow = base._lazy_clone()
|
|
data_ptr = cow.const_data_ptr()
|
|
uncovered = mod.covered_axes(cow, 100)
|
|
self.assertFalse(uncovered["eligible"])
|
|
self.assertTrue(torch._C._is_cow_tensor(cow))
|
|
self.assertEqual(cow.const_data_ptr(), data_ptr)
|
|
# Schema defaults come from the function signature itself.
|
|
self.assertEqual(
|
|
mod.covered_axes(x, 64), mod.covered_axes(x, 64, -1, True, True)
|
|
)
|
|
|
|
def test_covered_axes_selects_work_rungs(self):
|
|
import importlib.util
|
|
import os
|
|
|
|
path = os.path.join(
|
|
os.path.dirname(torch.__file__), "_native", "ops", "topk", "aot.py"
|
|
)
|
|
spec = importlib.util.spec_from_file_location("topk_aot_work_t", path)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
|
|
prior = torch.are_deterministic_algorithms_enabled()
|
|
try:
|
|
torch.use_deterministic_algorithms(True)
|
|
for n, k, expected in (
|
|
(4100, 64, (mod._ARCH_TAIL_ITERS, None)),
|
|
(5120, 512, (4, 2)),
|
|
(6144, 512, (mod._ARCH_TAIL_ITERS, None)),
|
|
(36860, 1024, (mod._ARCH_TAIL_ITERS, None)),
|
|
):
|
|
x = torch.empty(M, n, device="cuda")
|
|
axes = mod.covered_axes(x, k)
|
|
self.assertEqual(
|
|
(axes["scalar_tail_iters"], axes["fixed_vec_iters"]), expected
|
|
)
|
|
self.assertTrue(axes["eligible"])
|
|
finally:
|
|
torch.use_deterministic_algorithms(prior)
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_flags_the_stub_declines_are_uncovered(self):
|
|
# The stub takes only dim=last, largest and sorted; coverage must agree, or such
|
|
# a call declines on both routes and lands on stock aten instead of the JIT one.
|
|
from torch._native import aot_manifest
|
|
|
|
x = torch.empty(M, 4096, dtype=torch.float32, device="cuda")
|
|
self.assertTrue(aot_manifest.covers("topk", "CUDA", (x, 64), {}))
|
|
for kwargs in ({"dim": 0}, {"largest": False}, {"sorted": False}):
|
|
with self.subTest(**kwargs):
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, 64), kwargs))
|
|
# dim spelled as the last axis is the covered case, however it is written.
|
|
self.assertTrue(aot_manifest.covers("topk", "CUDA", (x, 64), {"dim": 1}))
|
|
|
|
@skipIfNoNativeAot("topk")
|
|
def test_manifest_covers_matches_grid(self):
|
|
# CUDA tensors, because coverage includes the prelude's full-wave M gate, so
|
|
# a CPU probe is always uncovered.
|
|
from torch._native import aot_manifest
|
|
|
|
for n in GRID_N:
|
|
for k in GRID_K:
|
|
x = torch.empty(M, n, dtype=torch.float32, device="cuda")
|
|
self.assertTrue(aot_manifest.covers("topk", "CUDA", (x, k), {}))
|
|
for k, n in (
|
|
(16, 64),
|
|
(64, 3072),
|
|
(64, 4100),
|
|
(512, 4096),
|
|
(512, 6140),
|
|
(1024, 32772),
|
|
(1024, 36860),
|
|
):
|
|
x = torch.empty(M, n, dtype=torch.float32, device="cuda")
|
|
self.assertTrue(aot_manifest.covers("topk", "CUDA", (x, k), {}))
|
|
for k, n in JIT_ONLY_REGISTER:
|
|
x = torch.empty(M, n, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, k), {}))
|
|
from torch._native.ops.topk.aot import _radix_min_n
|
|
|
|
major = torch.cuda.get_device_capability()[0]
|
|
min_n = _radix_min_n("float32", 64, major)
|
|
x = torch.empty(M, min_n - 4, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, 64), {}))
|
|
x = torch.empty(M, 4101, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, 64), {}))
|
|
x = torch.empty(M, 4092, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, 512), {}))
|
|
x = torch.empty(M, 32764, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (x, 1024), {}))
|
|
xh = torch.empty(M, 4096, dtype=torch.float16, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (xh, 64), {}))
|
|
# Below the full-wave gate: on-grid but NOT covered (JIT keeps it).
|
|
xs = torch.empty(4, 4096, dtype=torch.float32, device="cuda")
|
|
self.assertFalse(aot_manifest.covers("topk", "CUDA", (xs, 64), {}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
run_tests()
|