operators/examples/007_heuristics.ipynb
get_operators can return many operators that are all correct for your
problem. Without a heuristic, that list is in discovery order — not
"fastest first."
A heuristic can optionally rank the estimated performance of candidate operators, and reorders and prunes the candidates accordingly.
CUTLASS Operator API natively supports NVIDIA Matmul Heuristics (nvMatmulHeuristics)
to do such ranking directly in get_operators()
nvidia-matmul-heuristicsNVIDIA Matmul Heuristics (nvMatmulHeuristics) is an optional dependency of CUTLASS Operator API, and must be present to run heuristics.
Install: pip install 'nvidia-cutlass-operators[heuristics]'
Currently, heuristics integration is only supported for non-blockscaled GEMMs on SM100.
If you request a heuristic and ranking cannot run (missing optional package, unsupported args), an error is raised.
import sys
import torch
import cutlass.operators as ops
from cutlass.operators.heuristics.nvmatmul import is_available as nvmatmul_available
if not (status := ops.utils.device.device_or_env_supports("100")):
print(f"This notebook expects an SM100-class GPU.\n{status.error}")
sys.exit(0)
if not nvmatmul_available():
print(
"nvmatmul heuristic is unavailable. Install with:\n"
" pip install 'nvidia-cutlass-operators[heuristics]'"
)
sys.exit(0)
Operator API can create a nvMatmulHeuristics instance using ops.get_heuristic("nvmatmul")(gpu="B200").
gpu= names the exact GPU SKU to model and defaults to "B200" if omitted.
get_operators() then natively supports using this heuristic returning candidate
operators for given arguments.
Additionally, we can use limit=N to limit the results to the top-N heuristic-recommended Operators.
Currently, it supports only supports non-blockscaled GEMMs for Blackwell (SM100) GPUs.
When supported, get_operators(args, heuristic) will:
M, N, K = 4096, 4096, 4096
A = torch.randn(M, K, device="cuda", dtype=torch.float16)
B = torch.randn(K, N, device="cuda", dtype=torch.float16)
out = torch.empty(M, N, device="cuda", dtype=torch.float16)
args = ops.GemmArguments(A, B, out, accumulator_type=torch.float32)
heuristic = ops.get_heuristic("nvmatmul")(gpu="B200")
# or, equivalently:
heuristic = ops.heuristics.NvMatmulHeuristics(gpu="B200")
operators = ops.get_operators(
args,
target_sm="100a",
providers=[ops.CuTeDSLProvider],
heuristic=heuristic,
limit=5,
)
print(f"Returned {len(operators)} operator(s) (limit=5)")
operators[0].run(args)
An error is raised if the ranking cannot run at all, e.g. for missing package, unsupported args, and unsupported GPU (currently only SM100 is supported).
try:
ops.get_heuristic("nvmatmul")(gpu="H100_SXM")
except ValueError as e:
print(f"Unsupported GPU: {e}")
We do a quick benchmark below to compare the operators returned by heuristics.
def benchmark_operator(op: ops.Operator, args: ops.GemmArguments, warmup=10, iters=50):
"""Return the median GPU time (in ms) for `op.run(args)`."""
compiled = op.compile(args)
for _ in range(warmup):
op.run(args, compiled_artifact=compiled, assume_supported_args=True)
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
times_ms = []
for _ in range(iters):
start.record()
op.run(args, compiled_artifact=compiled, assume_supported_args=True)
end.record()
torch.cuda.synchronize()
times_ms.append(start.elapsed_time(end))
return sorted(times_ms)[len(times_ms) // 2]
unsorted_operators = ops.get_operators(args, target_sm="100a", providers=[ops.providers.CuTeDSLProvider])
sorted_operators = ops.get_operators(args, target_sm="100a", providers=[ops.providers.CuTeDSLProvider], heuristic=heuristic)
print(f"{len(unsorted_operators)} total operator(s) for this problem\n")
print(f"After applying heuristics, this was sorted and pruned to {len(sorted_operators)} operator(s).\n")
candidates = {
"fastest heuristic-recommended operator": sorted_operators[0],
"slowest heuristic-recommended operator": sorted_operators[-1],
"arbitrary operator": unsorted_operators[0],
}
for label, op in candidates.items():
median_ms = benchmark_operator(op, args)
print(f"{label}: {median_ms:.4f} ms \t (name: {op.metadata.operator_name})")
Please note that heuristics-based ranking is an estimate. It is helpful to limit and guide the search to promising candidates to top-N recommendations, and may not yield a definitive single winner.