Spaces:
Running
Running
File size: 6,748 Bytes
e313085 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | """KernelBench task loading, train/eval split, and prompt building."""
import hashlib
import os
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Dict, Tuple
EVAL_FRACTION_PCT = 10 # held-out share of each level
@dataclass(frozen=True)
class Task:
task_id: str # "level1/19_ReLU"
level: int
src: str # reference source: Model, get_inputs, get_init_inputs
@property
def split(self) -> str:
h = int(hashlib.sha1(self.task_id.encode()).hexdigest(), 16)
return "eval" if h % 100 < EVAL_FRACTION_PCT else "train"
@lru_cache(maxsize=None)
def load_tasks(root: str = "", levels: Tuple[int, ...] = (1, 2, 3)) -> Dict[str, Task]:
"""Load `<root>/level{N}/*.py` (KernelBench layout). Root defaults to $KERNELBENCH_DIR."""
base = Path(root or os.environ.get("KERNELBENCH_DIR", ""))
if not base.is_dir():
raise FileNotFoundError(f"KernelBench dir not found: {base!s} (set KERNELBENCH_DIR)")
tasks = {}
for level in levels:
for f in sorted((base / f"level{level}").glob("*.py")):
tid = f"level{level}/{f.stem}"
tasks[tid] = Task(tid, level, f.read_text())
if not tasks:
raise FileNotFoundError(f"No KernelBench tasks under {base}")
return tasks
RULES = {
"triton": (
"Write custom Triton kernels (`@triton.jit`) and call them from `ModelNew.forward`."
),
"cuda": (
"Write custom CUDA kernels compiled with `torch.utils.cpp_extension.load_inline` "
"and call them from `ModelNew.forward`."
),
}
def build_prompt(task: Task, language: str = "triton", max_turns: int = 4) -> str:
"""Prompt version from KERNEL_ENV_PROMPT ("2" default; "1" = the prompt used for the v1-v3 baselines)."""
if os.environ.get("KERNEL_ENV_PROMPT", "2") == "1" or language != "triton":
prompt = _prompt_v1(task, language, max_turns)
else:
prompt = _prompt_v2(task, max_turns)
if task.task_id in {"level1/23_Softmax", "level1/37_FrobeniusNorm_"}:
prompt += ("\nValidation for this normalization task also uses constant, peaked or signed inputs, "
"atol=min(configured_atol, max(abs(reference))*1e-5), the configured rtol, "
"and normalization invariants. "
"Softmax rows must sum to 1; Frobenius normalization uses one global norm, not per-slice norms.")
if task.task_id in {'level1/19_ReLU', 'level1/20_LeakyReLU', 'level1/31_ELU',
'level1/29_Softplus', 'level1/88_MinGPTNewGelu'}:
prompt += ('\nValidation also uses signed inputs in [-32,32] and zeros at the original shape. '
'Implement the full activation, including negative inputs; timing uses the original generator.')
return prompt
def _prompt_v1(task: Task, language: str, max_turns: int) -> str:
return f"""You are optimizing a PyTorch model with custom GPU kernels.
Reference implementation (task {task.task_id}):
```python
{task.src.strip()}
```
Write a Python module defining `class ModelNew(nn.Module)` with the same `__init__` signature
and the same outputs as `Model`, but faster. {RULES[language]}
Rules:
- Allowed imports: torch, triton (and triton.language), math, typing, functools.
- Outside kernels, only allocate tensors (torch.empty/zeros/... ) and reshape/view them;
no torch compute ops, torch.nn.functional, nn layer calls, or torch.compile.
- Parameters may be held in nn modules / nn.Parameter created in `__init__`.
- Outputs are checked on random inputs (atol=rtol=1e-2), then timed against the reference.
- You get {max_turns} submissions; each returns compile errors, mismatch info, or timings.
Keep reasoning short and code comments brief: replies are capped in length, and a reply cut off
before the module is complete is rejected. Reply with the complete module source only."""
# v2: a complete valid answer for a toy reference `torch.relu(x * self.scale)`. Baselines v1-v3 showed about a
# third of turns rejected for computing with PyTorch outside kernels, i.e. the rules were not understood.
EXAMPLE_V2 = """import torch
import torch.nn as nn
import triton
import triton.language as tl
@triton.jit
def scale_relu_kernel(x_ptr, w_ptr, y_ptr, n, d, BLOCK: tl.constexpr):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
x = tl.load(x_ptr + offs, mask=mask)
w = tl.load(w_ptr + offs % d, mask=mask)
tl.store(y_ptr + offs, tl.maximum(x * w, 0.0), mask=mask)
class ModelNew(nn.Module):
def __init__(self, dim):
super().__init__()
self.scale = nn.Parameter(torch.ones(dim)) # same parameters as Model, created the same way
def forward(self, x):
x = x.contiguous()
y = torch.empty_like(x) # allocating is allowed; all math happens in the kernel
n = x.numel()
scale_relu_kernel[(triton.cdiv(n, 1024),)](x, self.scale, y, n, x.shape[-1], BLOCK=1024)
return y"""
def _prompt_v2(task: Task, max_turns: int) -> str:
return f"""Rewrite this PyTorch model with custom Triton GPU kernels so it gives the same outputs, faster.
Reference implementation (task {task.task_id}):
```python
{task.src.strip()}
```
Rules (a submission that breaks one is rejected before it runs):
1. Define a top-level `class ModelNew(nn.Module)` with the same `__init__` arguments as `Model`. Create the same
nn modules and parameters in the same order, so weights initialize identically.
2. All math runs inside `@triton.jit` kernels. In `forward` and helpers you may only allocate outputs
(torch.empty, torch.empty_like, torch.zeros), call .contiguous()/.view()/.reshape(), read .shape/.stride(),
and launch kernels.
3. Never call PyTorch compute outside kernels: no torch.matmul, torch.relu, torch.softmax, torch.sum, etc.,
no torch.nn.functional (F.*), no `@`, no tensor methods like x.sum() or x.clamp(), and never call a layer
such as self.conv(x) or self.linear(x). To use a layer's weights, pass self.conv.weight and
self.conv.bias to your kernel.
4. Imports allowed: torch, torch.nn, triton, triton.language, math, typing, functools. Do not include
Model, get_inputs or get_init_inputs.
5. Outputs are checked on random inputs (atol=rtol=1e-2), then timed against the reference.
You get {max_turns} submissions; each returns errors, mismatches or timings.
Example: for a reference computing `torch.relu(x * self.scale)`, a valid answer is:
```python
{EXAMPLE_V2}
```
Answer with one ```python block containing the complete module and nothing else. Keep it short (under about
150 lines, brief comments): replies are length-capped, and a reply cut off before the module ends is rejected."""
|