KernelBench mega · RTX PRO 6000
rl grid ppo Kimi K3 (1M)
manually audited: clean
Clean and megakernel-authentic at 28.7578x (718,944,533 SPS vs the 25,000,000 SPS anchor). The solution runs the ENTIRE PPO training run — all iterations of the 32-step x 4096-env rollout, GAE, advantage normalization, and 4 epochs x 4 minibatches of exact PPO gradient + grad-norm clip + Adam — inside ONE persistent cooperative kernel launch (plus a one-off init kernel), with parameters and Adam moments resident in shared memory for the whole run. Seed-keyed Philox RNG drives weight init, env resets, action sampling, and food respawns; a Feistel bijection replaces randperm for exact balanced minibatch assignment. No caching keyed on inputs, no CUDA graphs, no torch.compile, no RL library, no check.py sniffing, and all grader templates byte-match the snapshot. This 28.7578 is NOT a duplicate of any other run: the sibling 20260716_150001 run has peak_fraction=null (its check.py timed out waiting on the GPU lock) and a different solution file (md5 6a850095479b4ac27e046d4fa82d0ab1 vs this run's a63618ba936472df072c942b6e783e69).
Kernel source (redacted)
"""Grid-foraging PPO training megakernel (solution).
The ENTIRE training run -- all PPO iterations, each containing the full
32-step x 4096-env rollout and the full 4-epoch x 4-minibatch PPO update --
runs inside ONE persistent cooperative kernel launch (plus a tiny one-off
init kernel for weight initialization and a single final curve readback).
Kernel launches therefore do not scale with env-steps, horizon, or
minibatches: there are two launches per *run*, not per iteration.
Inside the launch, per PPO iteration:
* Rollout phase (fused): every 16-lane half-warp owns one env and advances
it through all 32 horizon steps: policy forward (the 645 parameters sit
in shared memory; the 64 hidden units are split 4-per-lane and the
logits/value are formed by a 16-lane butterfly reduction), categorical
sampling from counter-based Philox RNG, env transition + food respawn,
then an in-register backward GAE scan (no bootstrap past the horizon,
matching the reference). Packed {state, logp, adv, ret} records (16B per
sample) are written to a global buffer; advantage mean/var statistics
and the episodic-return curve are accumulated via atomics.
* Update phase (fused): 4 epochs x 4 minibatches. Sample--minibatch
assignment is an exact balanced bijection: each warp sweeps consecutive
positions of a Feistel permutation of all 2^17 samples (no randperm
materialization, near-perfectly balanced warps; statistically equivalent
to the reference's randperm-chunk partitioning). For every sample, the
fused forward+backward pass computes the exact PPO gradient terms (clip
with the reference's tie semantics, entropy, and value terms), kept in
per-lane register accumulators. Per-warp partials are staged in shared
memory and block-reduced, one cooperative grid.sync() per minibatch,
then every block redundantly applies the identical grad-norm clip +
Adam step to its own shared-memory parameter copy (identical math =>
identical copies, so no broadcast barrier is needed). Parameters and
Adam moments live in shared memory for the whole run.
Advantage normalization uses the same global (unbiased) mean/std statistic
as the reference. Custom CUDA via load_inline. No CUDA graphs, no
torch.compile, no RL library.
"""
from __future__ import annotations
import os
import torch
from torch.utils.cpp_extension import load_inline
os.environ.setdefault("TORCH_CUDA_ARCH_LIST", "12.0")
_CPP_SRC = "at::Tensor train_impl(int64_t total_env_steps, int64_t seed);"
_CUDA_SRC = r"""
#include <ATen/ATen.h>
#include <c10/cuda/CUDAStream.h>
#include <cooperative_groups.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdint.h>
namespace cg = cooperative_groups;
#define N_ENVS 4096
#define TT 32
#define N_SAMP 131072 // N_ENVS * TT == 2^17
#define NMB_SAMP 32768 // N_SAMP / 4 == 2^15
#define NPARAM 645 // 64*4 + 64 + 4*64 + 4 + 64 + 1
#define INV_NMB (1.0f / 32768.0f)
#define TPB 384 // threads per block in the megakernel
#define NWARP_B (TPB / 32) // warps per block
// parameter vector layout
// W1 [0,256) j*4+i (hidden j, input i)
// b1 [256,320) j
// W2 [320,576) c*64+j (action c, hidden j)
// b2 [576,580) c
// w3 [580,644) j
// b3 644
__device__ __forceinline__ float tanh_ap(float x) {
float y;
asm("tanh.approx.f32 %0, %1;" : "=f"(y) : "f"(x));
return y;
}
__device__ __forceinline__ uint4 philox4x32_10(uint4 c, uint2 k) {
#pragma unroll
for (int i = 0; i < 10; ++i) {
unsigned long long p0 = (unsigned long long)c.x * 0xD2511F53ull;
unsigned long long p1 = (unsigned long long)c.z * 0xCD9E8D57ull;
uint4 n;
n.x = (unsigned)(p1 >> 32) ^ c.y ^ k.x;
n.y = (unsigned)p1;
n.z = (unsigned)(p0 >> 32) ^ c.w ^ k.y;
n.w = (unsigned)p0;
c = n;
k.x += 0x9E3779B9u;
k.y += 0xBB67AE85u;
}
return c;
}
__device__ __forceinline__ uint32_t feistel17(int pos, uint32_t multA, uint32_t addB) {
uint32_t x = (uint32_t)pos & 0x1FFFFu;
x ^= x >> 7;
x = (x * multA) & 0x1FFFFu;
x ^= x >> 5;
x = (x + addB) & 0x1FFFFu;
x ^= x >> 8;
return x;
}
__device__ __forceinline__ float uni01(uint32_t r) {
return (float)r * 0x1.0p-32f; // [0,1)
}
// ---------------------------------------------------------------------------
// Weight initialization: uniform(-1/sqrt(fan_in), 1/sqrt(fan_in)), biases zero.
// Separate one-off launch (does not scale with iterations).
// ---------------------------------------------------------------------------
__global__ void init_kernel(float* __restrict__ params, uint32_t k0, uint32_t k1) {
uint2 key = make_uint2(k0 ^ 0xF00DF00Du, k1 ^ 0x12345678u);
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i * 4 >= NPARAM) return;
uint4 r = philox4x32_10(make_uint4(i, 0xABCDu, 0, 0), key);
#pragma unroll
for (int q = 0; q < 4; ++q) {
int idx = i * 4 + q;
if (idx >= NPARAM) break;
float u = uni01((&r.x)[q]);
float w;
if (idx < 256) w = (u * 2.f - 1.f) * 0.5f; // W1 bound 1/sqrt(4)
else if (idx < 320) w = 0.f; // b1
else if (idx < 576) w = (u * 2.f - 1.f) * 0.125f; // W2 bound 1/sqrt(64)
else if (idx < 580) w = 0.f; // b2
else if (idx < 644) w = (u * 2.f - 1.f) * 0.125f; // w3
else w = 0.f; // b3
params[idx] = w;
}
}
// ---------------------------------------------------------------------------
// Persistent shared state of the megakernel.
// ---------------------------------------------------------------------------
struct MegaShared {
float p[NPARAM]; // parameters
float m[NPARAM]; // Adam m
float v[NPARAM]; // Adam v
float red[32]; // reduction scratch
double bc[2]; // beta^t accumulators (fp64, incremental)
float bcf[2]; // lr/(1-b1^t), 1/(1-b2^t) for this step
float slice[NWARP_B][NPARAM]; // per-warp gradient partials (lane-disjoint slots)
float vsh[2 * NWARP_B][TT + 1]; // per-half-warp rollout values
};
// ---------------------------------------------------------------------------
// THE megakernel: an entire PPO training run in one cooperative launch.
// ---------------------------------------------------------------------------
__global__ void __launch_bounds__(TPB, 1) ppo_megakernel(const float* __restrict__ init_params,
uint4* __restrict__ traj, // [N_SAMP] packed {sa,logp,adv,ret}
double* __restrict__ cum_stats, // [0,1] cum adv sum/sumsq, [2,3] prev
float* __restrict__ gradG, // [16*NPARAM]
float* __restrict__ curve, // [iters] reward sums
int iters, uint32_t k0, uint32_t k1) {
cg::grid_group grid = cg::this_grid();
extern __shared__ char smem_raw[];
MegaShared& S = *reinterpret_cast<MegaShared*>(smem_raw);
const int tid = threadIdx.x;
const int lane = tid & 31;
const int wid = tid >> 5;
const int warp_g = blockIdx.x * NWARP_B + wid;
const int nwarp = gridDim.x * NWARP_B;
uint2 key = make_uint2(k0, k1);
// load initial parameters; Adam moments start at zero (host pre-zeroed)
for (int i = tid; i < NPARAM; i += TPB) {
S.p[i] = init_params[i];
S.m[i] = 0.f;
S.v[i] = 0.f;
}
if (tid == 0) { S.bc[0] = 1.0; S.bc[1] = 1.0; }
__syncthreads();
for (int iter = 0; iter < iters; ++iter) {
// ------------------------------------------------------------------
// ROLLOUT PHASE: half-warp (16 lanes) per env, full HORIZON inside
// the launch. Hidden-unit quads per lane; logits/value by butterfly.
// ------------------------------------------------------------------
for (int i = blockIdx.x * TPB + tid; i < 16 * NPARAM; i += gridDim.x * TPB)
gradG[i] = 0.f; // cheap: spread across all threads while rolling out
const int half_r = lane >> 4;
const int lh_r = lane & 15;
// register-cache this lane's weights for the whole rollout phase
const float w2_00 = S.p[320 + lh_r], w2_01 = S.p[320 + lh_r + 16], w2_02 = S.p[320 + lh_r + 32], w2_03 = S.p[320 + lh_r + 48];
const float w2_10 = S.p[384 + lh_r], w2_11 = S.p[384 + lh_r + 16], w2_12 = S.p[384 + lh_r + 32], w2_13 = S.p[384 + lh_r + 48];
const float w2_20 = S.p[448 + lh_r], w2_21 = S.p[448 + lh_r + 16], w2_22 = S.p[448 + lh_r + 32], w2_23 = S.p[448 + lh_r + 48];
const float w2_30 = S.p[512 + lh_r], w2_31 = S.p[512 + lh_r + 16], w2_32 = S.p[512 + lh_r + 32], w2_33 = S.p[512 + lh_r + 48];
const float w3_0 = S.p[580 + lh_r], w3_1 = S.p[580 + lh_r + 16], w3_2 = S.p[580 + lh_r + 32], w3_3 = S.p[580 + lh_r + 48];
const float w1_00 = S.p[lh_r * 4 + 0], w1_01 = S.p[lh_r * 4 + 1], w1_02 = S.p[lh_r * 4 + 2], w1_03 = S.p[lh_r * 4 + 3];
const float w1_10 = S.p[(lh_r + 16) * 4 + 0], w1_11 = S.p[(lh_r + 16) * 4 + 1], w1_12 = S.p[(lh_r + 16) * 4 + 2], w1_13 = S.p[(lh_r + 16) * 4 + 3];
const float w1_20 = S.p[(lh_r + 32) * 4 + 0], w1_21 = S.p[(lh_r + 32) * 4 + 1], w1_22 = S.p[(lh_r + 32) * 4 + 2], w1_23 = S.p[(lh_r + 32) * 4 + 3];
const float w1_30 = S.p[(lh_r + 48) * 4 + 0], w1_31 = S.p[(lh_r + 48) * 4 + 1], w1_32 = S.p[(lh_r + 48) * 4 + 2], w1_33 = S.p[(lh_r + 48) * 4 + 3];
const float b1_0 = S.p[256 + lh_r], b1_1 = S.p[256 + lh_r + 16], b1_2 = S.p[256 + lh_r + 32], b1_3 = S.p[256 + lh_r + 48];
const float b2_0 = S.p[576 + 0], b2_1 = S.p[576 + 1], b2_2 = S.p[576 + 2], b2_3 = S.p[576 + 3];
const float b3c = S.p[644];
float ws_ret = 0.f, ws_asum = 0.f, ws_asq = 0.f;
for (int e = warp_g * 2 + half_r; e < N_ENVS; e += nwarp * 2) {
// episode reset (board re-randomizes every HORIZON; rollout aligns)
uint4 rr = philox4x32_10(make_uint4(e, (uint32_t)iter, 0x5151u, 0u), key);
int ax = rr.x % 11, ay = rr.y % 11, fx = rr.z % 11, fy = rr.w % 11;
uint32_t rewmask = 0;
float retsum = 0.f;
#pragma unroll
for (int t = 0; t < TT; ++t) {
float X0 = (float)(fx - ax) * (1.0f / 11.0f);
float X1 = (float)(fy - ay) * (1.0f / 11.0f);
float X2 = (float)ax * 0.1f;
float X3 = (float)ay * 0.1f;
float h0 = tanh_ap(w1_00 * X0 + w1_01 * X1 + w1_02 * X2 + w1_03 * X3 + b1_0);
float h1 = tanh_ap(w1_10 * X0 + w1_11 * X1 + w1_12 * X2 + w1_13 * X3 + b1_1);
float h2 = tanh_ap(w1_20 * X0 + w1_21 * X1 + w1_22 * X2 + w1_23 * X3 + b1_2);
float h3 = tanh_ap(w1_30 * X0 + w1_31 * X1 + w1_32 * X2 + w1_33 * X3 + b1_3);
float z0 = w2_00 * h0 + w2_01 * h1 + w2_02 * h2 + w2_03 * h3;
float z1 = w2_10 * h0 + w2_11 * h1 + w2_12 * h2 + w2_13 * h3;
float z2 = w2_20 * h0 + w2_21 * h1 + w2_22 * h2 + w2_23 * h3;
float z3 = w2_30 * h0 + w2_31 * h1 + w2_32 * h2 + w2_33 * h3;
float vv = w3_0 * h0 + w3_1 * h1 + w3_2 * h2 + w3_3 * h3;
#pragma unroll
for (int off = 8; off; off >>= 1) {
z0 += __shfl_xor_sync(0xffffffffu, z0, off);
z1 += __shfl_xor_sync(0xffffffffu, z1, off);
z2 += __shfl_xor_sync(0xffffffffu, z2, off);
z3 += __shfl_xor_sync(0xffffffffu, z3, off);
vv += __shfl_xor_sync(0xffffffffu, vv, off);
}
z0 += b2_0; z1 += b2_1; z2 += b2_2; z3 += b2_3;
vv += b3c;
float mx = fmaxf(fmaxf(z0, z1), fmaxf(z2, z3));
float e0 = __expf(z0 - mx), e1 = __expf(z1 - mx), e2 = __expf(z2 - mx), e3 = __expf(z3 - mx);
float Sm = e0 + e1 + e2 + e3;
uint4 rn = philox4x32_10(make_uint4(e, (uint32_t)iter, (uint32_t)t, 0x717u), key);
float u = uni01(rn.x);
float rS = __frcp_rn(Sm);
float c0 = e0 * rS;
float c1 = c0 + e1 * rS;
float c2 = c1 + e2 * rS;
int a = (u < c0) ? 0 : ((u < c1) ? 1 : ((u < c2) ? 2 : 3));
float lse = mx + __logf(Sm);
float za = (a == 0) ? z0 : ((a == 1) ? z1 : ((a == 2) ? z2 : z3));
if (lh_r == 0) {
int s = t * N_ENVS + e;
uint32_t word = (uint32_t)ax | ((uint32_t)ay << 4) | ((uint32_t)fx << 8) |
((uint32_t)fy << 12) | ((uint32_t)a << 16);
*reinterpret_cast<uint2*>(&traj[s]) = make_uint2(word, __float_as_uint(za - lse));
}
S.vsh[(wid * 2 + half_r)][t] = vv;
// env transition (replicated across the half-warp)
int dx = (a == 2) ? -1 : ((a == 3) ? 1 : 0);
int dy = (a == 0) ? -1 : ((a == 1) ? 1 : 0);
ax += dx; ax = ax < 0 ? 0 : (ax > 10 ? 10 : ax);
ay += dy; ay = ay < 0 ? 0 : (ay > 10 ? 10 : ay);
bool hit = (ax == fx) && (ay == fy);
if (hit) { fx = (int)(rn.y % 11u); fy = (int)(rn.z % 11u); }
rewmask |= (uint32_t)hit << t;
retsum += hit ? 1.f : 0.f;
}
// backward GAE (episode terminates at T: no bootstrap)
__syncwarp(0xffffu << (half_r * 16)); // half-warp visibility of vsh
float gae = 0.f, advsum = 0.f, advsq = 0.f;
float nextv = 0.f, nt = 0.f;
#pragma unroll
for (int t = TT - 1; t >= 0; --t) {
float vv = S.vsh[(wid * 2 + half_r)][t];
float r = (float)((rewmask >> t) & 1u);
float delta = r + 0.99f * nextv * nt - vv;
gae = delta + (0.99f * 0.95f) * nt * gae;
nextv = vv;
nt = 1.f;
if (lh_r == 0) *reinterpret_cast<float2*>(&traj[t * N_ENVS + e].z) = make_float2(gae, gae + vv);
advsum += gae;
advsq += gae * gae;
}
if (lh_r == 0) {
ws_ret += retsum;
ws_asum += advsum;
ws_asq += advsq;
}
}
__syncwarp();
if (lh_r == 0) {
atomicAdd(&curve[iter], ws_ret);
atomicAdd(&cum_stats[0], (double)ws_asum);
atomicAdd(&cum_stats[1], (double)ws_asq);
}
grid.sync(); // rollout complete; gradG zeroed by everyone
// per-iteration advantage normalization stats (cumulative counters)
double d0 = cum_stats[0] - cum_stats[2];
double d1 = cum_stats[1] - cum_stats[3];
double dn = (double)N_SAMP;
float mu = (float)(d0 / dn);
float var = (float)((d1 - d0 * d0 / dn) / (dn - 1.0));
float inv_sig = 1.0f / (sqrtf(var) + 1e-8f);
// ------------------------------------------------------------------
// UPDATE PHASE: 4 epochs x 4 minibatches, one grid.sync per minibatch.
// ------------------------------------------------------------------
for (int mb = 0; mb < 16; ++mb) {
const int epoch = mb >> 2;
const int mbi = mb & 3;
const uint32_t salt = (uint32_t)(iter * 4 + epoch) * 0x9E3779B9u ^ k0;
const uint32_t multA = ((salt ^ (salt >> 11)) | 0x1u) & 0x1FFFFu;
const uint32_t addB = (salt * 0x85EBCA6Bu) & 0x1FFFFu;
float aW1[4][4], aW2[4][4], aV3[4], aB1[4], aB2[4], aB3;
#pragma unroll
for (int a = 0; a < 4; ++a) {
#pragma unroll
for (int b = 0; b < 4; ++b) { aW1[a][b] = 0.f; aW2[a][b] = 0.f; aB2[b] = 0.f; }
aV3[a] = 0.f; aB1[a] = 0.f;
}
aB3 = 0.f;
uint4 u4pre = make_uint4(0u, 0u, 0u, 0u);
// pull-based exact minibatch enumeration: each warp sweeps
// consecutive permutation positions through a forward Feistel
// bijection -> near-perfectly balanced samples per warp, no
// compaction pass and no shared hit buffer at all.
const int half = lane >> 4;
const int lh = lane & 15;
// register-cache this lane's W2/w3 weights for the whole minibatch
// (they change only at Adam; ~300 fewer shared loads per lane)
const float w2_00 = S.p[320 + lh], w2_01 = S.p[320 + lh + 16], w2_02 = S.p[320 + lh + 32], w2_03 = S.p[320 + lh + 48];
const float w2_10 = S.p[384 + lh], w2_11 = S.p[384 + lh + 16], w2_12 = S.p[384 + lh + 32], w2_13 = S.p[384 + lh + 48];
const float w2_20 = S.p[448 + lh], w2_21 = S.p[448 + lh + 16], w2_22 = S.p[448 + lh + 32], w2_23 = S.p[448 + lh + 48];
const float w2_30 = S.p[512 + lh], w2_31 = S.p[512 + lh + 16], w2_32 = S.p[512 + lh + 32], w2_33 = S.p[512 + lh + 48];
const float w3_0 = S.p[580 + lh], w3_1 = S.p[580 + lh + 16], w3_2 = S.p[580 + lh + 32], w3_3 = S.p[580 + lh + 48];
const float w1_00 = S.p[lh * 4 + 0], w1_01 = S.p[lh * 4 + 1], w1_02 = S.p[lh * 4 + 2], w1_03 = S.p[lh * 4 + 3];
const float w1_10 = S.p[(lh + 16) * 4 + 0], w1_11 = S.p[(lh + 16) * 4 + 1], w1_12 = S.p[(lh + 16) * 4 + 2], w1_13 = S.p[(lh + 16) * 4 + 3];
const float w1_20 = S.p[(lh + 32) * 4 + 0], w1_21 = S.p[(lh + 32) * 4 + 1], w1_22 = S.p[(lh + 32) * 4 + 2], w1_23 = S.p[(lh + 32) * 4 + 3];
const float w1_30 = S.p[(lh + 48) * 4 + 0], w1_31 = S.p[(lh + 48) * 4 + 1], w1_32 = S.p[(lh + 48) * 4 + 2], w1_33 = S.p[(lh + 48) * 4 + 3];
const float b1_0 = S.p[256 + lh], b1_1 = S.p[256 + lh + 16], b1_2 = S.p[256 + lh + 32], b1_3 = S.p[256 + lh + 48];
const float b2_0 = S.p[576 + 0], b2_1 = S.p[576 + 1], b2_2 = S.p[576 + 2], b2_3 = S.p[576 + 3];
const float b3c = S.p[644];
const int mbBeg = mbi * 32768;
const int mbEnd = mbBeg + 32768;
const int pStep = nwarp * 2;
// rolling prefetch of packed sample data (warm from cross-barrier
// prefetch for mb > 0)
uint4 u4c = (mb > 0) ? u4pre : make_uint4(0u, 0u, 0u, 0u);
if (mb == 0 && mbBeg + warp_g * 2 + half < mbEnd) {
int s0 = feistel17(mbBeg + warp_g * 2 + half, multA, addB);
u4c = traj[s0];
}
#pragma unroll 2
for (int pbase = mbBeg + warp_g * 2; pbase < mbEnd; pbase += pStep) {
int pos = pbase + half;
uint4 u4 = u4c;
uint2 u2 = make_uint2(u4.x, u4.y);
float2 f2 = make_float2(__uint_as_float(u4.z), __uint_as_float(u4.w));
int posn = pos + pStep;
if (posn < mbEnd) {
int sn = feistel17(posn, multA, addB);
u4c = traj[sn];
}
bool acth = (pos < mbEnd);
uint32_t sa = u2.x;
float oldlogp = __uint_as_float(u2.y);
int ax = sa & 15, ay = (sa >> 4) & 15, fx = (sa >> 8) & 15, fy = (sa >> 12) & 15;
int act = (sa >> 16) & 3;
float X0 = (float)(fx - ax) * (1.0f / 11.0f);
float X1 = (float)(fy - ay) * (1.0f / 11.0f);
float X2 = (float)ax * 0.1f;
float X3 = (float)ay * 0.1f;
float adv = (f2.x - mu) * inv_sig;
float rt = f2.y;
// 16 lanes x 4 hidden units
float h0 = tanh_ap(w1_00 * X0 + w1_01 * X1 + w1_02 * X2 + w1_03 * X3 + b1_0);
float h1 = tanh_ap(w1_10 * X0 + w1_11 * X1 + w1_12 * X2 + w1_13 * X3 + b1_1);
float h2 = tanh_ap(w1_20 * X0 + w1_21 * X1 + w1_22 * X2 + w1_23 * X3 + b1_2);
float h3 = tanh_ap(w1_30 * X0 + w1_31 * X1 + w1_32 * X2 + w1_33 * X3 + b1_3);
float z0 = w2_00 * h0 + w2_01 * h1 + w2_02 * h2 + w2_03 * h3;
float z1 = w2_10 * h0 + w2_11 * h1 + w2_12 * h2 + w2_13 * h3;
float z2 = w2_20 * h0 + w2_21 * h1 + w2_22 * h2 + w2_23 * h3;
float z3 = w2_30 * h0 + w2_31 * h1 + w2_32 * h2 + w2_33 * h3;
float vv = w3_0 * h0 + w3_1 * h1 + w3_2 * h2 + w3_3 * h3;
#pragma unroll
for (int off = 8; off; off >>= 1) {
z0 += __shfl_xor_sync(0xffffffffu, z0, off);
z1 += __shfl_xor_sync(0xffffffffu, z1, off);
z2 += __shfl_xor_sync(0xffffffffu, z2, off);
z3 += __shfl_xor_sync(0xffffffffu, z3, off);
vv += __shfl_xor_sync(0xffffffffu, vv, off);
}
z0 += b2_0; z1 += b2_1; z2 += b2_2; z3 += b2_3;
float v = vv + b3c;
float mx = fmaxf(fmaxf(z0, z1), fmaxf(z2, z3));
float e0 = __expf(z0 - mx), e1 = __expf(z1 - mx), e2 = __expf(z2 - mx), e3 = __expf(z3 - mx);
float Sm = e0 + e1 + e2 + e3;
float rS = __frcp_rn(Sm);
float p0 = e0 * rS, p1 = e1 * rS, p2 = e2 * rS, p3 = e3 * rS;
float lse = mx + __logf(Sm);
float lp0 = z0 - lse, lp1 = z1 - lse, lp2 = z2 - lse, lp3 = z3 - lse;
float HH = -(p0 * lp0 + p1 * lp1 + p2 * lp2 + p3 * lp3);
float newlp = act == 0 ? lp0 : (act == 1 ? lp1 : (act == 2 ? lp2 : lp3));
float ratio = __expf(newlp - oldlogp);
float s1 = ratio * adv;
float rc = fminf(fmaxf(ratio, 0.8f), 1.2f);
float s2 = rc * adv;
float pgfac = (s1 <= s2) ? (-ratio * adv * INV_NMB) : 0.f;
float entfac = 0.01f * INV_NMB;
float gz0 = pgfac * ((act == 0 ? 1.f : 0.f) - p0) + entfac * p0 * (lp0 + HH);
float gz1 = pgfac * ((act == 1 ? 1.f : 0.f) - p1) + entfac * p1 * (lp1 + HH);
float gz2 = pgfac * ((act == 2 ? 1.f : 0.f) - p2) + entfac * p2 * (lp2 + HH);
float gz3 = pgfac * ((act == 3 ? 1.f : 0.f) - p3) + entfac * p3 * (lp3 + HH);
float GV = (v - rt) * INV_NMB;
float dw0 = gz0 * w2_00 + gz1 * w2_10 + gz2 * w2_20 + gz3 * w2_30 + GV * w3_0;
float dw1 = gz0 * w2_01 + gz1 * w2_11 + gz2 * w2_21 + gz3 * w2_31 + GV * w3_1;
float dw2 = gz0 * w2_02 + gz1 * w2_12 + gz2 * w2_22 + gz3 * w2_32 + GV * w3_2;
float dw3 = gz0 * w2_03 + gz1 * w2_13 + gz2 * w2_23 + gz3 * w2_33 + GV * w3_3;
float dh0 = dw0 * (1.f - h0 * h0);
float dh1 = dw1 * (1.f - h1 * h1);
float dh2 = dw2 * (1.f - h2 * h2);
float dh3 = dw3 * (1.f - h3 * h3);
if (!acth) {
gz0 = 0.f; gz1 = 0.f; gz2 = 0.f; gz3 = 0.f; GV = 0.f;
dh0 = 0.f; dh1 = 0.f; dh2 = 0.f; dh3 = 0.f;
h0 = 0.f; h1 = 0.f; h2 = 0.f; h3 = 0.f;
}
aW2[0][0] += gz0 * h0; aW2[0][1] += gz1 * h0; aW2[0][2] += gz2 * h0; aW2[0][3] += gz3 * h0;
aW2[1][0] += gz0 * h1; aW2[1][1] += gz1 * h1; aW2[1][2] += gz2 * h1; aW2[1][3] += gz3 * h1;
aW2[2][0] += gz0 * h2; aW2[2][1] += gz1 * h2; aW2[2][2] += gz2 * h2; aW2[2][3] += gz3 * h2;
aW2[3][0] += gz0 * h3; aW2[3][1] += gz1 * h3; aW2[3][2] += gz2 * h3; aW2[3][3] += gz3 * h3;
aW1[0][0] += dh0 * X0; aW1[0][1] += dh0 * X1; aW1[0][2] += dh0 * X2; aW1[0][3] += dh0 * X3;
aW1[1][0] += dh1 * X0; aW1[1][1] += dh1 * X1; aW1[1][2] += dh1 * X2; aW1[1][3] += dh1 * X3;
aW1[2][0] += dh2 * X0; aW1[2][1] += dh2 * X1; aW1[2][2] += dh2 * X2; aW1[2][3] += dh2 * X3;
aW1[3][0] += dh3 * X0; aW1[3][1] += dh3 * X1; aW1[3][2] += dh3 * X2; aW1[3][3] += dh3 * X3;
aV3[0] += GV * h0; aV3[1] += GV * h1; aV3[2] += GV * h2; aV3[3] += GV * h3;
aB1[0] += dh0; aB1[1] += dh1; aB1[2] += dh2; aB1[3] += dh3;
if (lh == 0) {
aB2[0] += gz0; aB2[1] += gz1; aB2[2] += gz2; aB2[3] += gz3; aB3 += GV;
}
}
float* G = gradG + mb * NPARAM;
// halves hold disjoint samples but identical j-slot sets: reduce
// accumulator pairs (lane lh <-> lh+16) via shuffles, then only
// lanes 0..15 stage per-warp partials in shared.
#pragma unroll
for (int k = 0; k < 4; ++k) {
#pragma unroll
for (int q = 0; q < 4; ++q) {
aW1[k][q] += __shfl_xor_sync(0xffffffffu, aW1[k][q], 16);
aW2[k][q] += __shfl_xor_sync(0xffffffffu, aW2[k][q], 16);
}
aV3[k] += __shfl_xor_sync(0xffffffffu, aV3[k], 16);
aB1[k] += __shfl_xor_sync(0xffffffffu, aB1[k], 16);
aB2[k] += __shfl_xor_sync(0xffffffffu, aB2[k], 16);
}
aB3 += __shfl_xor_sync(0xffffffffu, aB3, 16);
if (lane < 16) {
float* SL = &S.slice[wid][0];
#pragma unroll
for (int k = 0; k < 4; ++k) {
int j = lane + 16 * k;
SL[j * 4 + 0] = aW1[k][0]; SL[j * 4 + 1] = aW1[k][1];
SL[j * 4 + 2] = aW1[k][2]; SL[j * 4 + 3] = aW1[k][3];
SL[256 + j] = aB1[k];
SL[320 + j] = aW2[k][0]; SL[384 + j] = aW2[k][1];
SL[448 + j] = aW2[k][2]; SL[512 + j] = aW2[k][3];
SL[580 + j] = aV3[k];
}
if (lane == 0) {
SL[576 + 0] = aB2[0]; SL[576 + 1] = aB2[1];
SL[576 + 2] = aB2[2]; SL[576 + 3] = aB2[3];
SL[644] = aB3;
}
}
__syncthreads();
// block-level reduction: sum the warp slices, one atomic per slot
for (int i = tid; i < NPARAM; i += TPB) {
float r = S.slice[0][i];
for (int w = 1; w < NWARP_B; ++w) r += S.slice[w][i];
atomicAdd(G + i, r);
}
// cross-barrier prefetch: next minibatch's first sample record is
// param-independent; start the gather before the grid sync.
u4pre = make_uint4(0u, 0u, 0u, 0u);
if (mb < 15) {
const int epn = (mb + 1) >> 2;
const int mbn = (mb + 1) & 3;
const uint32_t saltN = (uint32_t)(iter * 4 + epn) * 0x9E3779B9u ^ k0;
const uint32_t mAN = ((saltN ^ (saltN >> 11)) | 0x1u) & 0x1FFFFu;
const uint32_t aBN = (saltN * 0x85EBCA6Bu) & 0x1FFFFu;
int posn = mbn * 32768 + warp_g * 2 + half;
if (posn < mbn * 32768 + 32768) {
u4pre = traj[feistel17(posn, mAN, aBN)];
}
}
grid.sync(); // minibatch gradient complete (drains the few atomics)
// identical clip + Adam in every block
float g_loc[2];
float ssum = 0.f;
#pragma unroll
for (int q = 0; q < 2; ++q) {
int i = tid + q * TPB;
g_loc[q] = (i < NPARAM) ? G[i] : 0.f;
ssum += g_loc[q] * g_loc[q];
}
#pragma unroll
for (int off = 16; off; off >>= 1) ssum += __shfl_down_sync(0xffffffffu, ssum, off);
if (lane == 0) S.red[wid] = ssum;
if (tid == 0) {
S.bc[0] *= 0.9;
S.bc[1] *= 0.999;
S.bcf[0] = (float)(0.003 / (1.0 - S.bc[0]));
S.bcf[1] = (float)(1.0 / (1.0 - S.bc[1]));
}
__syncthreads();
if (tid == 0) {
float tot = 0.f;
for (int w = 0; w < NWARP_B; ++w) tot += S.red[w];
S.red[0] = sqrtf(tot);
}
__syncthreads();
float total = S.red[0];
float clipf = fminf(0.5f / (total + 1e-6f), 1.0f);
float bcA = S.bcf[0]; // lr / (1 - beta1^t)
float bc2i = S.bcf[1]; // 1 / (1 - beta2^t)
#pragma unroll
for (int q = 0; q < 2; ++q) {
int i = tid + q * TPB;
if (i < NPARAM) {
float g = g_loc[q] * clipf;
float m = 0.9f * S.m[i] + 0.1f * g;
float v2 = 0.999f * S.v[i] + 0.001f * g * g;
S.m[i] = m;
S.v[i] = v2;
S.p[i] -= (bcA * m) / (sqrtf(v2 * bc2i) + 1e-8f);
}
}
__syncthreads();
}
if (blockIdx.x == 0 && tid == 0) {
cum_stats[2] = cum_stats[0];
cum_stats[3] = cum_stats[1];
}
// next iteration reuses the shared-resident parameters directly
}
}
// ---------------------------------------------------------------------------
// Host orchestration: init launch + ONE megakernel launch + curve readback.
// ---------------------------------------------------------------------------
at::Tensor train_impl(int64_t total_env_steps, int64_t seed) {
const int64_t iters = std::max<int64_t>(1, total_env_steps / (TT * N_ENVS));
auto optsF = at::TensorOptions().dtype(at::kFloat).device(at::kCUDA, 0);
auto optsI = at::TensorOptions().dtype(at::kInt).device(at::kCUDA, 0);
auto optsD = at::TensorOptions().dtype(at::kDouble).device(at::kCUDA, 0);
at::Tensor params = at::zeros({NPARAM}, optsF);
at::Tensor traj = at::empty({N_SAMP, 4}, optsI);
at::Tensor gradG = at::empty({16 * NPARAM}, optsF);
at::Tensor cum_stats = at::zeros({4}, optsD);
at::Tensor curve = at::zeros({iters}, optsF);
cudaStream_t stream = at::cuda::getCurrentCUDAStream(0).stream();
const uint32_t k0 = (uint32_t)(seed & 0xffffffffu);
const uint32_t k1 = (uint32_t)(((uint64_t)seed >> 32) | 0x5A5A5A5Au);
float* p_params = params.data_ptr<float>();
uint4* p_traj = (uint4*)traj.data_ptr<int>();
float* p_gradG = gradG.data_ptr<float>();
double* p_stats = cum_stats.data_ptr<double>();
float* p_curve = curve.data_ptr<float>();
init_kernel<<<1, 256, 0, stream>>>(p_params, k0, k1);
static int mg_grid = -1;
if (mg_grid < 0) {
int dev = 0;
cudaGetDevice(&dev);
int sms = 0;
cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, dev);
cudaFuncSetAttribute(ppo_megakernel, cudaFuncAttributeMaxDynamicSharedMemorySize,
(int)sizeof(MegaShared));
int nb = 0;
cudaOccupancyMaxActiveBlocksPerMultiprocessor(&nb, ppo_megakernel, TPB, (int)sizeof(MegaShared));
TORCH_CHECK(nb >= 1, "megakernel does not fit on this GPU");
mg_grid = sms;
}
int iters_i = (int)iters;
void* args[] = {&p_params, &p_traj, &p_stats, &p_gradG,
&p_curve, &iters_i, (void*)&k0, (void*)&k1};
cudaError_t err = cudaLaunchCooperativeKernel(
(void*)ppo_megakernel, dim3(mg_grid), dim3(TPB), args, (size_t)sizeof(MegaShared), stream);
TORCH_CHECK(err == cudaSuccess, "cooperative launch failed: ",
cudaGetErrorString(err));
cudaError_t last = cudaGetLastError();
TORCH_CHECK(last == cudaSuccess, "kernel launch error: ", cudaGetErrorString(last));
at::Tensor cpu = curve.to(at::kCPU);
return cpu;
}
"""
_ext = None
def _build():
global _ext
if _ext is None:
_ext = load_inline(
name="grid_ppo_megakernel_v2",
cpp_sources=[_CPP_SRC],
cuda_sources=[_CUDA_SRC],
functions=["train_impl"],
extra_cuda_cflags=["-O3"],
verbose=False,
)
return _ext
def train(total_env_steps: int, seed: int) -> list[float]:
ext = _build()
cpu = ext.train_impl(int(total_env_steps), int(seed))
n = cpu.numel()
# curve tensor holds per-iteration reward sums; reference reports the mean
# over envs, so divide by NUM_ENVS.
return [cpu[i].item() / 4096.0 for i in range(n)]
if __name__ == "__main__":
import time
t0 = time.perf_counter()
c = train(32 * 4096 * 40, seed=0)
torch.cuda.synchronize()
dt = time.perf_counter() - t0
steps = 32 * 4096 * len(c)
print(f"iters={len(c)} return_first={c[0]:.3f} return_last={c[-1]:.3f}")
print(f"elapsed={dt:.2f}s sps={steps / dt:,.0f}")
20260716_233413_kinetic-claude_kinetic-0715_1m__01_rl_grid_ppo