KernelBench cuda · RTX PRO 6000
Grid + MinGRU SPS GPT-6 Luna
53.7%geomean peak fraction across shapes
Luna captured the Grid + MinGRU rollout in a CUDA graph with custom environment, observation, and MinGRU activation kernels. Autocast PyTorch matrix multiplies perform the gate projections. The graph keeps live parameter pointers and reinitializes agents, food, and RNG state on each replay.
harnesscodexagent session1h 7mtotal wall2h 34mcheck26sbenchmark2soutput tokens157,429gpu-lock wait1h 27mgpu-lock held37sregimethroughput
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
No per-shape benchmark data archived for this run.
Kernel source (redacted)
from __future__ import annotations
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
BOARD = 11
OBS_DIM = 4
HIDDEN = 256
GRU_LAYERS = 3
NUM_ACTIONS = 4
GRU_OUT = 3 * HIDDEN
class Model(nn.Module):
def __init__(self):
super().__init__()
self.w_enc = nn.Parameter(torch.empty(HIDDEN, OBS_DIM))
self.b_enc = nn.Parameter(torch.zeros(HIDDEN))
self.w_gru = nn.Parameter(torch.empty(GRU_LAYERS, GRU_OUT, HIDDEN))
self.w_a = nn.Parameter(torch.empty(NUM_ACTIONS, HIDDEN))
self.b_a = nn.Parameter(torch.zeros(NUM_ACTIONS))
self.w_v = nn.Parameter(torch.empty(1, HIDDEN))
self.b_v = nn.Parameter(torch.zeros(1))
self.reset_parameters(0)
def reset_parameters(self, seed: int = 0) -> None:
g = torch.Generator(device="cpu")
g.manual_seed(seed)
for p in self.parameters():
tmp = torch.empty(p.shape, dtype=p.dtype, device="cpu")
tmp.normal_(0.0, 0.02, generator=g)
p.data.copy_(tmp)
def forward(self, obs: torch.Tensor, state: torch.Tensor):
return policy_forward(self, obs, state)
_CUDA_CPP = r"""
#include <torch/extension.h>
#include <vector>
std::vector<torch::Tensor> env_step_cuda(torch::Tensor agent,
torch::Tensor food,
torch::Tensor actions,
torch::Tensor rng_state);
std::vector<torch::Tensor> env_step_logits_cuda(torch::Tensor agent,
torch::Tensor food,
torch::Tensor logits,
torch::Tensor rng_state);
torch::Tensor observe_cuda(torch::Tensor agent, torch::Tensor food);
torch::Tensor mingru_cuda(torch::Tensor gates, torch::Tensor h,
torch::Tensor state, torch::Tensor new_state,
int64_t layer);
torch::Tensor mingru_half_cuda(torch::Tensor gates, torch::Tensor h,
torch::Tensor state, torch::Tensor new_state,
int64_t layer);
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("env_step", &env_step_cuda, "Grid-foraging environment step (CUDA)");
m.def("env_step_logits", &env_step_logits_cuda,
"Greedy policy action and environment step (CUDA)");
m.def("observe", &observe_cuda, "Grid-foraging observation (CUDA)");
m.def("mingru", &mingru_cuda, "MinGRU activation (CUDA)");
m.def("mingru_half", &mingru_half_cuda,
"MinGRU activation with FP16 gates and hidden state (CUDA)");
}
"""
_CUDA_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <cstdint>
#include <vector>
__global__ void move_agents(const float* agent, const float* food,
const int64_t* actions, const float* logits,
bool use_logits, float* agent_out,
float* reward, int* any_hit, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i >= n) return;
int x = __float2int_rn(agent[2 * i]);
int y = __float2int_rn(agent[2 * i + 1]);
int a;
if (use_logits) {
int action = 0;
float best = logits[4 * i];
for (int k = 1; k < 4; ++k) {
float v = logits[4 * i + k];
if (v > best) {
best = v;
action = k;
}
}
a = action;
} else {
a = static_cast<int>(actions[i]);
}
if (a == 0) --y;
else if (a == 1) ++y;
else if (a == 2) --x;
else if (a == 3) ++x;
x = max(0, min(10, x));
y = max(0, min(10, y));
agent_out[2 * i] = static_cast<float>(x);
agent_out[2 * i + 1] = static_cast<float>(y);
bool hit = (static_cast<float>(x) == food[2 * i]) &&
(static_cast<float>(y) == food[2 * i + 1]);
reward[i] = hit ? 1.0f : 0.0f;
if (hit) atomicExch(any_hit, 1);
}
__device__ __forceinline__ uint64_t lcg(uint64_t x) {
return (x * 6364136223846793005ULL + 1ULL) & 0x7fffffffffffffffULL;
}
__device__ __forceinline__ void store_obs(const float* agent,
const float* food,
float* obs, int i) {
float ax = agent[2 * i];
float ay = agent[2 * i + 1];
obs[4 * i] = __fdiv_rn(food[2 * i] - ax, 11.0f);
obs[4 * i + 1] = __fdiv_rn(food[2 * i + 1] - ay, 11.0f);
obs[4 * i + 2] = __fdiv_rn(ax, 10.0f);
obs[4 * i + 3] = __fdiv_rn(ay, 10.0f);
}
__global__ void observe_kernel(const float* agent, const float* food,
float* obs, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) store_obs(agent, food, obs, i);
}
__global__ void mingru_kernel(const float* gates, const float* h,
const float* state, float* new_state,
float* h_out, int64_t state_stride0,
int64_t state_stride1, int layer, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
int total = n * 256;
if (i >= total) return;
int row = i >> 8;
int col = i & 255;
float zh = gates[row * 768 + col];
float zg = gates[row * 768 + 256 + col];
float zp = gates[row * 768 + 512 + col];
float st = state[row * state_stride0 + layer * state_stride1 + col];
float sh = 1.0f / (1.0f + expf(-zg));
float candidate = tanhf(zh);
float out = __fadd_rn(st, __fmul_rn(sh, __fsub_rn(candidate, st)));
float p = 1.0f / (1.0f + expf(-zp));
float mixed = __fadd_rn(__fmul_rn(p, out),
__fmul_rn(__fsub_rn(1.0f, p), h[i]));
new_state[row * 768 + layer * 256 + col] = out;
h_out[i] = mixed;
}
__global__ void mingru_half_kernel(const __half* gates, const __half* h,
const __half* state, __half* new_state,
__half* h_out, int64_t state_stride0,
int64_t state_stride1, int layer, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
int total = n * 256;
if (i >= total) return;
int row = i >> 8;
int col = i & 255;
float zh = __half2float(gates[row * 768 + col]);
float zg = __half2float(gates[row * 768 + 256 + col]);
float zp = __half2float(gates[row * 768 + 512 + col]);
float st = __half2float(state[row * state_stride0 + layer * state_stride1 + col]);
float sh = 1.0f / (1.0f + expf(-zg));
float candidate = tanhf(zh);
float out = __fadd_rn(st, __fmul_rn(sh, __fsub_rn(candidate, st)));
float p = 1.0f / (1.0f + expf(-zp));
float mixed = __fadd_rn(__fmul_rn(p, out),
__fmul_rn(__fsub_rn(1.0f, p),
__half2float(h[i])));
new_state[row * 768 + layer * 256 + col] = __float2half_rn(out);
h_out[i] = __float2half_rn(mixed);
}
__global__ void respawn_food(const float* food, const float* reward,
const float* agent, const int64_t* rng_in,
const int* any_hit,
float* food_out, float* obs_out,
int64_t* rng_out, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i >= n) return;
float fx = food[2 * i];
float fy = food[2 * i + 1];
uint64_t r = static_cast<uint64_t>(rng_in[i]);
if (*any_hit) {
r = lcg(r);
if (reward[i] != 0.0f) {
fx = static_cast<float>(r % 11ULL);
}
r = lcg(r);
if (reward[i] != 0.0f) {
fy = static_cast<float>(r % 11ULL);
}
}
food_out[2 * i] = fx;
food_out[2 * i + 1] = fy;
rng_out[i] = static_cast<int64_t>(r);
store_obs(agent, food_out, obs_out, i);
}
static std::vector<torch::Tensor> env_step_impl(
torch::Tensor agent, torch::Tensor food, const int64_t* actions,
const float* logits, bool use_logits, torch::Tensor rng_state) {
c10::cuda::CUDAGuard guard(agent.device());
const int n = static_cast<int>(agent.size(0));
auto agent_out = torch::empty_like(agent);
auto food_out = torch::empty_like(food);
auto obs_out = torch::empty({n, 4}, agent.options());
auto reward = torch::empty({n}, agent.options());
auto rng_out = torch::empty_like(rng_state);
auto flag = torch::empty({1}, agent.options().dtype(torch::kInt32));
auto stream = at::cuda::getCurrentCUDAStream();
cudaMemsetAsync(flag.data_ptr<int>(), 0, sizeof(int), stream);
constexpr int threads = 256;
const int blocks = (n + threads - 1) / threads;
move_agents<<<blocks, threads, 0, stream>>>(
agent.data_ptr<float>(), food.data_ptr<float>(),
actions, logits, use_logits, agent_out.data_ptr<float>(),
reward.data_ptr<float>(), flag.data_ptr<int>(), n);
respawn_food<<<blocks, threads, 0, stream>>>(
food.data_ptr<float>(), reward.data_ptr<float>(), agent_out.data_ptr<float>(),
rng_state.data_ptr<int64_t>(), flag.data_ptr<int>(),
food_out.data_ptr<float>(), obs_out.data_ptr<float>(),
rng_out.data_ptr<int64_t>(), n);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return {agent_out, food_out, reward, rng_out, obs_out};
}
std::vector<torch::Tensor> env_step_cuda(torch::Tensor agent,
torch::Tensor food,
torch::Tensor actions,
torch::Tensor rng_state) {
return env_step_impl(agent, food, actions.data_ptr<int64_t>(), nullptr,
false, rng_state);
}
std::vector<torch::Tensor> env_step_logits_cuda(torch::Tensor agent,
torch::Tensor food,
torch::Tensor logits,
torch::Tensor rng_state) {
return env_step_impl(agent, food, nullptr, logits.data_ptr<float>(),
true, rng_state);
}
torch::Tensor observe_cuda(torch::Tensor agent, torch::Tensor food) {
c10::cuda::CUDAGuard guard(agent.device());
const int n = static_cast<int>(agent.size(0));
auto obs = torch::empty({n, 4}, agent.options());
constexpr int threads = 256;
const int blocks = (n + threads - 1) / threads;
auto stream = at::cuda::getCurrentCUDAStream();
observe_kernel<<<blocks, threads, 0, stream>>>(
agent.data_ptr<float>(), food.data_ptr<float>(), obs.data_ptr<float>(), n);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return obs;
}
torch::Tensor mingru_cuda(torch::Tensor gates, torch::Tensor h,
torch::Tensor state, torch::Tensor new_state,
int64_t layer) {
c10::cuda::CUDAGuard guard(h.device());
auto h_out = torch::empty_like(h);
const int n = static_cast<int>(h.size(0));
constexpr int threads = 256;
const int blocks = (n * 256 + threads - 1) / threads;
auto stream = at::cuda::getCurrentCUDAStream();
mingru_kernel<<<blocks, threads, 0, stream>>>(
gates.data_ptr<float>(), h.data_ptr<float>(), state.data_ptr<float>(),
new_state.data_ptr<float>(), h_out.data_ptr<float>(),
state.stride(0), state.stride(1), static_cast<int>(layer), n);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return h_out;
}
torch::Tensor mingru_half_cuda(torch::Tensor gates, torch::Tensor h,
torch::Tensor state, torch::Tensor new_state,
int64_t layer) {
c10::cuda::CUDAGuard guard(h.device());
auto h_out = torch::empty_like(h);
const int n = static_cast<int>(h.size(0));
constexpr int threads = 256;
const int blocks = (n * 256 + threads - 1) / threads;
auto stream = at::cuda::getCurrentCUDAStream();
mingru_half_kernel<<<blocks, threads, 0, stream>>>(
reinterpret_cast<const __half*>(gates.data_ptr<at::Half>()),
reinterpret_cast<const __half*>(h.data_ptr<at::Half>()),
reinterpret_cast<const __half*>(state.data_ptr<at::Half>()),
reinterpret_cast<__half*>(new_state.data_ptr<at::Half>()),
reinterpret_cast<__half*>(h_out.data_ptr<at::Half>()),
state.stride(0), state.stride(1), static_cast<int>(layer), n);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return h_out;
}
"""
_EXT = None
_GRAPH_CACHE: dict[tuple, tuple] = {}
def _cuda_ext():
global _EXT
if _EXT is None:
from torch.utils.cpp_extension import load_inline
build_directory = os.path.join(
os.path.dirname(__file__), ".torch_extensions", "grid_mingru_env_sm120_v6"
)
os.makedirs(build_directory, exist_ok=True)
_EXT = load_inline(
name="grid_mingru_env_sm120_v6",
build_directory=build_directory,
cpp_sources=_CUDA_CPP,
cuda_sources=_CUDA_SRC,
functions=None,
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3", "-arch=sm_120"],
with_cuda=True,
verbose=False,
)
return _EXT
def policy_forward(model: Model, obs: torch.Tensor, state: torch.Tensor):
h = F.linear(obs, model.w_enc, model.b_enc)
new_states = []
for layer in range(GRU_LAYERS):
st = state[:, layer, :]
gates = F.linear(h, model.w_gru[layer])
zh, zg, zp = gates.split(HIDDEN, dim=-1)
out = st + torch.sigmoid(zg) * (torch.tanh(zh) - st)
p = torch.sigmoid(zp)
h = p * out + (1.0 - p) * h
new_states.append(out)
new_state = torch.stack(new_states, dim=1)
logits = F.linear(h, model.w_a, model.b_a)
value = F.linear(h, model.w_v, model.b_v).squeeze(-1)
return logits, new_state, value
def _policy_forward_rollout(model, obs, state):
with torch.autocast(device_type="cuda", dtype=torch.float16):
h = F.linear(obs, model.w_enc, model.b_enc)
new_state = torch.empty((obs.shape[0], GRU_LAYERS, HIDDEN),
device=obs.device, dtype=torch.float16)
for layer in range(GRU_LAYERS):
gates = F.linear(h, model.w_gru[layer])
h = _cuda_ext().mingru_half(gates, h, state, new_state, layer)
logits = F.linear(h, model.w_a, model.b_a)
return logits.float(), new_state
def env_step(agent, food, actions, rng_state):
return tuple(_cuda_ext().env_step(agent, food, actions, rng_state)[:4])
def _rollout(model, agent, food, rng_state, state, horizon):
rewards = torch.zeros(agent.shape[0], device=agent.device, dtype=torch.float32)
last_logits = torch.empty((agent.shape[0], NUM_ACTIONS),
device=agent.device, dtype=torch.float32)
obs = _cuda_ext().observe(agent, food)
for _ in range(horizon):
logits, state = _policy_forward_rollout(model, obs, state)
last_logits = logits
agent, food, r, rng_state, obs = _cuda_ext().env_step_logits(
agent, food, logits, rng_state
)
rewards = rewards + r
return rewards, agent, food, rng_state, state, last_logits
def _make_graph(model, num_envs, horizon, device):
_cuda_ext()
static_agent = torch.zeros((num_envs, 2), device=device, dtype=torch.float32)
static_food = torch.ones((num_envs, 2), device=device, dtype=torch.float32)
static_rng = torch.arange(num_envs, device=device, dtype=torch.int64)
static_state = torch.zeros((num_envs, GRU_LAYERS, HIDDEN),
device=device, dtype=torch.float16)
stream = torch.cuda.Stream(device=device)
stream.wait_stream(torch.cuda.current_stream(device))
old_tf32 = torch.backends.cuda.matmul.allow_tf32
torch.backends.cuda.matmul.allow_tf32 = True
try:
with torch.cuda.stream(stream), torch.inference_mode():
for _ in range(2):
_rollout(model, static_agent, static_food, static_rng,
static_state, min(horizon, 2))
torch.cuda.current_stream(device).wait_stream(stream)
graph = torch.cuda.CUDAGraph()
with torch.inference_mode(), torch.cuda.graph(graph):
captured = _rollout(model, static_agent, static_food, static_rng,
static_state, horizon)
finally:
torch.backends.cuda.matmul.allow_tf32 = old_tf32
return (graph, static_agent, static_food, static_rng,
captured[0], captured[1], static_state, model, captured[5])
def run(num_envs: int, horizon: int, seed: int, model: Model | None = None) -> dict:
device = torch.device("cuda:0")
if model is None:
model = Model()
model = model.to(device).eval()
_cuda_ext()
params_key = tuple(p.data_ptr() for p in model.parameters())
key = (id(model), params_key, num_envs, horizon, device.index)
entry = _GRAPH_CACHE.get(key)
if entry is None:
entry = _make_graph(model, num_envs, horizon, device)
_GRAPH_CACHE[key] = entry
graph, static_agent, static_food, static_rng, out_rewards, out_agent, _static_state, _keep_model, out_logits = entry
g = torch.Generator(device="cpu")
g.manual_seed(seed)
agent = torch.randint(0, BOARD, (num_envs, 2), generator=g).float()
food = torch.randint(0, BOARD, (num_envs, 2), generator=g).float()
static_agent.copy_(agent, non_blocking=True)
static_food.copy_(food, non_blocking=True)
static_rng.copy_(torch.arange(num_envs, device=device, dtype=torch.int64) + seed * 10007)
graph.replay()
return {
"rewards": out_rewards.clone(),
"positions": out_agent.long(),
"last_logits": out_logits.clone(),
}
20260922_123423_codex_gpt-6-luna_04_grid_mingru_sps