KernelBench mega · RTX PRO 6000
rl grid ppo Kimi K3 (256k)
manually audited: clean
Highest-scrutiny static audit finds this 20.7177x RTX PRO 6000 headline cell clean and megakernel-authentic. The banked solution is a real one-launch, seed-driven grid-foraging PPO trainer: it initializes a fresh policy and Adam state, simulates all 4096 environments for all 32 rollout steps, computes GAE and normalized advantages, and performs 4 epochs x 4 minibatch updates inside one cooperative CUDA kernel. It has no constant curve, output lookup, CUDA graph, compile wrapper, forbidden RL library, reference import, or host loop over rollout/update phases. The host does reuse process-global scratch tensors rather than physically allocating new tensors on each train() call, but the kernel clears every score/state accumulator and overwrites every rollout record before use, so each call logically recomputes from the live seed and cannot inherit a trained policy or return curve. One non-hacking semantic discrepancy is present: dv is coded as VF_COEF*(v-ret)/MB and therefore omits the factor of two from the derivative of VF_COEF*mean((v-ret)^2), making the value-loss gradient half the reference's. It does not remove any training phase or explain the speed, and all fresh-seed correctness and benchmark trials learn to the reference return level. No GPU rerun was performed for this audit; recomputation was established from code structure and archived grading evidence.
Kernel source (redacted)
"""Megakernel PPO training for a vectorized grid-foraging task.
The whole training run -- from-scratch policy init, per-iteration rollout
(all 32 env steps x 4096 envs + policy forward + sampling + GAE), and the
PPO update (4 epochs x 4 minibatches of the clipped loss + grad-norm clip +
Adam) -- executes inside ONE persistent cooperative kernel launch. Between
phases the kernel synchronizes the grid with cooperative groups; the host
only launches once and reads back the per-iteration mean episodic returns.
Everything (env, policy forward/backward, GAE, optimizer) is hand-written
CUDA -- no RL library is involved.
Kernel structure per iteration (17 grid syncs):
* rollout phase: warp-per-env; 32 sequential env steps, each computing the
policy forward for its env (lanes split the 64 hidden units), categorical
sampling, env transition, reward, with values/rewards kept in lane
registers by step index; the GAE back-sweep runs in-warp with shuffle
broadcasts; per-warp stat sums are block-reduced into global doubles.
* 16 x (gradient accumulation phase, grid sync, post phase). The post phase
(grad-norm clip + Adam) runs redundantly in every block against shared
memory state (bitwise identical), so minibatch k+1 follows minibatch k
with NO intervening grid sync. Gradient buffers rotate mod 4 and are
zeroed two minibatches ahead, and the advantage stats buffer ping-pongs
by iteration parity so no trailing iteration-boundary sync is needed.
Parameter layout (645 floats, matching the reference MLP Linear layout):
W1 [0..256) 64x4 (out,in) | b1 [256..320) | W2 [320..576) 4x64 (pi) |
b2 [576..580) | wv [580..644) (value) | bv [644].
Weights ~ U(-bound, bound), bound = 1/sqrt(fan_in); biases zero.
RNG: counter-based Philox4x32-10 keyed by the run seed; disjoint counter tags
for env resets, per-step sampling/respawn, weight init, and epoch permutation.
Minibatch permutation per epoch is an unbiased bijection over Z_2^17,
q = (a*s + b) mod 2^17 (odd a, offset b redrawn per epoch), which partitions
the batch into four equal minibatches exactly like a reshuffle.
"""
import torch
from torch.utils.cpp_extension import load_inline
_EXT = None
_CUDA_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cooperative_groups.h>
#include <cuda_runtime.h>
namespace cg = cooperative_groups;
#define NUM_ENVS 4096
#define HORIZON 32
#define NSAMP 131072
#define MB 32768
#define NPARAM 645
#define NTHREADS 128
#define NWARP (NTHREADS / 32)
#define GAMMA 0.99f
#define GAE_LAM 0.95f
#define CLIP 0.2f
#define ENT_COEF 0.01f
#define VF_COEF 0.5f
#define LR 3.0e-3f
#define MAX_GRAD_NORM 0.5f
__device__ __forceinline__ uint4 philox_round(uint4 c, uint2 k) {
unsigned lo0 = 0xD2511F53u * c.x, hi0 = __umulhi(0xD2511F53u, c.x);
unsigned lo1 = 0xCD9E8D57u * c.z, hi1 = __umulhi(0xCD9E8D57u, c.z);
return make_uint4(hi1 ^ c.y ^ k.x, lo1, hi0 ^ c.w ^ k.y, lo0);
}
__device__ __forceinline__ uint4 philox4x32_10(uint4 counter, uint2 key) {
#pragma unroll
for (int i = 0; i < 10; ++i) {
counter = philox_round(counter, key);
key.x += 0x9E3779B9u;
key.y += 0xBB67AE85u;
}
return counter;
}
__device__ __forceinline__ unsigned xnext_(uint4& st) {
unsigned r = __funnelshift_l(st.x + st.w, st.x + st.w, 7) + st.x;
unsigned t = st.y << 9;
st.z ^= st.x; st.w ^= st.y; st.y ^= st.z; st.x ^= st.w;
st.z ^= t; st.w = __funnelshift_l(st.w, st.w, 11);
return r;
}
__device__ __forceinline__ float u01(unsigned u) {
return (float)(u >> 8) * (1.0f / 16777216.0f);
}
// policy forward for one sample; lanes j1=lane, j2=lane+32 own hidden units.
// returns h (this lane's two units), plus z0..z3, v via warp reduction.
__device__ __forceinline__ void fwd(
float4 x, const float* __restrict__ th,
float& h1, float& h2, float& z0, float& z1, float& z2, float& z3, float& v,
int j1, int j2) {
h1 = th[256 + j1];
h1 = fmaf(th[j1 * 4 + 0], x.x, h1);
h1 = fmaf(th[j1 * 4 + 1], x.y, h1);
h1 = fmaf(th[j1 * 4 + 2], x.z, h1);
h1 = fmaf(th[j1 * 4 + 3], x.w, h1);
h1 = tanhf(h1);
h2 = th[256 + j2];
h2 = fmaf(th[j2 * 4 + 0], x.x, h2);
h2 = fmaf(th[j2 * 4 + 1], x.y, h2);
h2 = fmaf(th[j2 * 4 + 2], x.z, h2);
h2 = fmaf(th[j2 * 4 + 3], x.w, h2);
h2 = tanhf(h2);
float zp0 = th[320 + j1] * h1 + th[320 + j2] * h2;
float zp1 = th[384 + j1] * h1 + th[384 + j2] * h2;
float zp2 = th[448 + j1] * h1 + th[448 + j2] * h2;
float zp3 = th[512 + j1] * h1 + th[512 + j2] * h2;
float vp = th[580 + j1] * h1 + th[580 + j2] * h2;
#pragma unroll
for (int off = 16; off; off >>= 1) {
zp0 += __shfl_xor_sync(0xffffffffu, zp0, off);
zp1 += __shfl_xor_sync(0xffffffffu, zp1, off);
zp2 += __shfl_xor_sync(0xffffffffu, zp2, off);
zp3 += __shfl_xor_sync(0xffffffffu, zp3, off);
vp += __shfl_xor_sync(0xffffffffu, vp, off);
}
z0 = zp0 + th[576];
z1 = zp1 + th[577];
z2 = zp2 + th[578];
z3 = zp3 + th[579];
v = vp + th[644];
}
__global__ void __launch_bounds__(NTHREADS) train_kernel(
float4* __restrict__ rec, // NSAMP x {obs, [logp, actf, adv, ret]} pairs
float* __restrict__ grad,
float* __restrict__ theta_g,
float* __restrict__ curve,
double* __restrict__ dstats, // 2 parity x 2
unsigned seed_lo, unsigned seed_hi,
int iters) {
cg::grid_group grid = cg::this_grid();
// dynamic shared memory carve-out (static limit is 48KB; we need ~49.3KB)
extern __shared__ float smem[];
float* s_w2t = smem; // 256 (16B-aligned: holds float4 rows)
float* s_theta = s_w2t + 256; // NPARAM (16B-aligned: holds float4 rows)
float* s_m = s_theta + NPARAM; // NPARAM
float* s_v = s_m + NPARAM; // NPARAM
float* s_hbase = s_v + NPARAM + 1; // NWARP * (32*65) (16B-aligned)
float* s_dzbase = s_hbase + NWARP * (32 * 65);
float* s_xbase = s_dzbase + NWARP * (32 * 8);
float* s_red = s_xbase + NWARP * (32 * 4);
float* s_scalar = s_red + NWARP * 4;
const int tid = threadIdx.x;
const int gtid = blockIdx.x * NTHREADS + tid;
const int nthr = gridDim.x * NTHREADS;
const int nwarps = gridDim.x * NWARP;
const int wid = blockIdx.x * NWARP + (tid >> 5);
const int lane = tid & 31;
const int wib = tid >> 5;
const uint2 key = make_uint2(seed_lo, seed_hi);
// --- INIT ---------------------------------------------------------------
for (int p = gtid; p < NPARAM; p += nthr) {
float val = 0.0f;
if (p < 256) {
float u = u01(philox4x32_10(make_uint4(p, 0xC0DE0001u, 0, 0), key).x);
val = (2.0f * u - 1.0f) * 0.5f;
} else if ((p >= 320 && p < 576) || (p >= 580 && p < 644)) {
float u = u01(philox4x32_10(make_uint4(p, 0xC0DE0001u, 0, 0), key).x);
val = (2.0f * u - 1.0f) * 0.125f;
}
theta_g[p] = val;
}
for (int p = gtid; p < 4 * NPARAM; p += nthr) grad[p] = 0.0f;
if (gtid < 4) dstats[gtid] = 0.0;
for (int i = gtid; i < iters; i += nthr) curve[i] = 0.0f;
grid.sync();
for (int p = tid; p < NPARAM; p += NTHREADS) {
s_theta[p] = theta_g[p];
s_m[p] = 0.0f;
s_v[p] = 0.0f;
}
for (int p = tid; p < 256; p += NTHREADS)
s_w2t[p] = s_theta[320 + (p & 3) * 64 + (p >> 2)];
grid.sync();
const int j1 = lane;
const int j2 = lane + 32;
double pow1 = 0.9, pow2 = 0.999; // beta^t trackers for Adam bias correction
for (int iter = 0; iter < iters; ++iter) {
const int par = iter & 1;
// --- rollout + GAE + stats (warp per env) ------------------------------
float accS1 = 0.0f, accS2 = 0.0f, accRew = 0.0f;
for (int ee = wid * 4; ee < NUM_ENVS; ee += nwarps * 4) {
int ev[4];
uint4 rr[4];
#pragma unroll
for (int i = 0; i < 4; ++i) {
ev[i] = ee + i;
rr[i] = philox4x32_10(make_uint4(ev[i] < NUM_ENVS ? ev[i] : 0, 0xA000u + (unsigned)iter, 0, 0), key);
}
int ax[4], ay[4], fx[4], fy[4];
#pragma unroll
for (int i = 0; i < 4; ++i) {
ax[i] = (int)(u01(rr[i].x) * 11.0f);
ay[i] = (int)(u01(rr[i].y) * 11.0f);
fx[i] = (int)(u01(rr[i].z) * 11.0f);
fy[i] = (int)(u01(rr[i].w) * 11.0f);
}
uint4 rng[4];
#pragma unroll
for (int i = 0; i < 4; ++i)
rng[i] = make_uint4(rr[i].x | 1u, rr[i].y ^ 0x9E3779B9u, rr[i].z + 0x6A09E667u, rr[i].w | 0x3C6EF372u);
float valA[4], rewA[4];
#pragma unroll
for (int i = 0; i < 4; ++i) { valA[i] = 0.0f; rewA[i] = 0.0f; }
for (int t = 0; t < HORIZON; ++t) {
float zz[4][4];
float vv[4], lpv[4];
int aa[4];
#pragma unroll
for (int i = 0; i < 4; ++i) {
float h1, h2, z0, z1, z2, z3, v;
fwd(make_float4(
(float)(fx[i] - ax[i]) * (1.0f / 11.0f),
(float)(fy[i] - ay[i]) * (1.0f / 11.0f),
(float)ax[i] * 0.1f,
(float)ay[i] * 0.1f), s_theta, h1, h2, z0, z1, z2, z3, v, j1, j2);
float m = fmaxf(fmaxf(z0, z1), fmaxf(z2, z3));
float e0 = __expf(z0 - m), e1 = __expf(z1 - m), e2 = __expf(z2 - m), e3 = __expf(z3 - m);
float se = e0 + e1 + e2 + e3;
float u = u01(xnext_(rng[i])) * se;
int a = 0;
float cum = e0;
if (u > cum) { cum += e1; a = 1; }
if (u > cum) { cum += e2; a = 2; }
if (u > cum) { a = 3; }
zz[i][0] = z0; zz[i][1] = z1; zz[i][2] = z2; zz[i][3] = z3;
vv[i] = v;
aa[i] = a;
float za = (a == 0) ? z0 : (a == 1) ? z1 : (a == 2) ? z2 : z3;
lpv[i] = za - m - __logf(se);
}
if (lane == 0) {
#pragma unroll
for (int i = 0; i < 4; ++i) {
if (ev[i] < NUM_ENVS) {
int q = t * NUM_ENVS + ev[i];
float4 m4;
rec[q * 2] = make_float4(
(float)(fx[i] - ax[i]) * (1.0f / 11.0f),
(float)(fy[i] - ay[i]) * (1.0f / 11.0f),
(float)ax[i] * 0.1f,
(float)ay[i] * 0.1f);
m4.x = lpv[i]; m4.y = (float)aa[i];
rec[q * 2 + 1] = m4;
}
}
}
#pragma unroll
for (int i = 0; i < 4; ++i) {
int nx = ax[i] + (aa[i] == 3) - (aa[i] == 2);
int ny = ay[i] + (aa[i] == 1) - (aa[i] == 0);
nx = min(max(nx, 0), 10);
ny = min(max(ny, 0), 10);
float rew = 0.0f;
if (nx == fx[i] && ny == fy[i]) {
rew = 1.0f;
fx[i] = (int)(u01(xnext_(rng[i])) * 11.0f);
fy[i] = (int)(u01(xnext_(rng[i])) * 11.0f);
}
ax[i] = nx; ay[i] = ny;
rewA[i] = lane == t ? rew : rewA[i];
valA[i] = lane == t ? vv[i] : valA[i];
}
}
#pragma unroll
for (int i = 0; i < 4; ++i) {
if (ev[i] >= NUM_ENVS) break;
float gae = 0.0f, adv_t = 0.0f, ret_t = 0.0f;
for (int t = HORIZON - 1; t >= 0; --t) {
float ntf = (t < HORIZON - 1) ? 1.0f : 0.0f;
float vt = __shfl_sync(0xffffffffu, valA[i], t);
float rt = __shfl_sync(0xffffffffu, rewA[i], t);
float vt1 = __shfl_sync(0xffffffffu, valA[i], (t < HORIZON - 1) ? t + 1 : t);
float delta = rt + GAMMA * vt1 * ntf - vt;
gae = delta + GAMMA * GAE_LAM * ntf * gae;
if (lane == t) { adv_t = gae; ret_t = gae + vt; }
}
int q = lane * NUM_ENVS + ev[i];
float4 m4 = rec[q * 2 + 1];
m4.z = adv_t;
m4.w = ret_t;
rec[q * 2 + 1] = m4;
accS1 += adv_t;
accS2 += adv_t * adv_t;
accRew += rewA[i];
}
}
// block-reduce the three accumulators
#pragma unroll
for (int off = 16; off; off >>= 1) {
accS1 += __shfl_down_sync(0xffffffffu, accS1, off);
accS2 += __shfl_down_sync(0xffffffffu, accS2, off);
accRew += __shfl_down_sync(0xffffffffu, accRew, off);
}
if (lane == 0) {
s_red[wib * 4 + 0] = accS1;
s_red[wib * 4 + 1] = accS2;
s_red[wib * 4 + 2] = accRew;
}
__syncthreads();
if (tid < 3) {
double acc = 0.0;
for (int w = 0; w < NWARP; ++w) acc += (double)s_red[w * 4 + tid];
if (tid < 2) atomicAdd(&dstats[par * 2 + tid], acc);
else atomicAdd(&curve[iter], (float)acc);
}
grid.sync();
// --- PPO update ---------------------------------------------------------
double* ds = dstats + par * 2;
if (tid < 2) s_scalar[tid] = (float)ds[tid];
__syncthreads();
float mean = s_scalar[0] * (1.0f / NSAMP);
float var = (float)(((double)s_scalar[1] - (double)s_scalar[0] * (double)mean) / (NSAMP - 1.0));
float invstd = 1.0f / (sqrtf(var) + 1e-8f);
for (int epoch = 0; epoch < 4; ++epoch) {
uint4 pr = philox4x32_10(make_uint4((unsigned)(iter * 4 + epoch), 0xD000u, 0, 0), key);
const unsigned aperm = ((pr.x & 0xFFFFu) << 1) | 1u;
const unsigned bperm = pr.y & 0x1FFFFu;
for (int k = 0; k < 4; ++k) {
const int gmb = epoch * 4 + k;
const int gb = gmb & 3;
float gW1a[4] = {0.f, 0.f, 0.f, 0.f}, gW1b[4] = {0.f, 0.f, 0.f, 0.f};
float gb1a = 0.f, gb1b = 0.f;
float gW2a[4] = {0.f, 0.f, 0.f, 0.f}, gW2b[4] = {0.f, 0.f, 0.f, 0.f};
float gb2 = 0.f, gwva = 0.f, gwvb = 0.f, gbv = 0.f;
float* hT = s_hbase + wib * (32 * 65);
float* dzT = s_dzbase + wib * (32 * 8);
float* xT = s_xbase + wib * (32 * 4);
const float4* w1q = reinterpret_cast<const float4*>(s_theta);
const float4* w2q = reinterpret_cast<const float4*>(s_w2t);
const int mbstart = k * MB;
// tile = 32 samples per warp iteration; thread-per-sample inside
for (int slot0 = mbstart + wid * 32; slot0 < mbstart + MB; slot0 += nwarps * 32) {
const int slot = slot0 + lane;
const unsigned q = (aperm * (unsigned)slot + bperm) & 0x1FFFFu;
float4 x = rec[q * 2];
float4 m4 = rec[q * 2 + 1];
const int a = (int)m4.y;
const float lpo = m4.x;
const float ad = (m4.z - mean) * invstd;
const float rt = m4.w;
xT[lane * 4 + 0] = x.x; xT[lane * 4 + 1] = x.y;
xT[lane * 4 + 2] = x.z; xT[lane * 4 + 3] = x.w;
float z0 = s_theta[576], z1 = s_theta[577], z2 = s_theta[578], z3 = s_theta[579];
float v = s_theta[644];
#pragma unroll 4
for (int j = 0; j < 64; ++j) {
float4 w1 = w1q[j];
float acc = s_theta[256 + j];
acc = fmaf(w1.x, x.x, acc);
acc = fmaf(w1.y, x.y, acc);
acc = fmaf(w1.z, x.z, acc);
acc = fmaf(w1.w, x.w, acc);
float hj = tanhf(acc);
hT[lane * 65 + j] = hj;
float4 w2 = w2q[j];
z0 = fmaf(w2.x, hj, z0);
z1 = fmaf(w2.y, hj, z1);
z2 = fmaf(w2.z, hj, z2);
z3 = fmaf(w2.w, hj, z3);
v = fmaf(s_theta[580 + j], hj, v);
}
float m = fmaxf(fmaxf(z0, z1), fmaxf(z2, z3));
float e0 = __expf(z0 - m), e1 = __expf(z1 - m), e2 = __expf(z2 - m), e3 = __expf(z3 - m);
float se = e0 + e1 + e2 + e3;
float inv = 1.0f / se;
float p0 = e0 * inv, p1 = e1 * inv, p2 = e2 * inv, p3 = e3 * inv;
float lg = __logf(se);
float lp_new = ((a == 0) ? z0 : (a == 1) ? z1 : (a == 2) ? z2 : z3) - m - lg;
float r = __expf(lp_new - lpo);
bool active = (ad > 0.f) ? (r < (1.0f + CLIP)) : (r > (1.0f - CLIP));
float g = active ? -ad * r : 0.0f;
float ls0 = z0 - m - lg, ls1 = z1 - m - lg, ls2 = z2 - m - lg, ls3 = z3 - m - lg;
float H = -(p0 * ls0 + p1 * ls1 + p2 * ls2 + p3 * ls3);
const float sc = 1.0f / (float)MB;
float dz0 = sc * (g * ((a == 0) - p0) + ENT_COEF * p0 * (ls0 + H));
float dz1 = sc * (g * ((a == 1) - p1) + ENT_COEF * p1 * (ls1 + H));
float dz2 = sc * (g * ((a == 2) - p2) + ENT_COEF * p2 * (ls2 + H));
float dz3 = sc * (g * ((a == 3) - p3) + ENT_COEF * p3 * (ls3 + H));
float dv = sc * VF_COEF * (v - rt);
dzT[lane * 8 + 0] = dz0; dzT[lane * 8 + 1] = dz1;
dzT[lane * 8 + 2] = dz2; dzT[lane * 8 + 3] = dz3;
dzT[lane * 8 + 4] = dv;
__syncwarp();
#pragma unroll
for (int t = 0; t < 32; ++t) {
float h1v = hT[t * 65 + j1];
float h2v = hT[t * 65 + j2];
float4 dz4 = ((const float4*)dzT)[t * 2];
float d0 = dz4.x, d1 = dz4.y, d2 = dz4.z, d3 = dz4.w;
float dvv = dzT[t * 8 + 4];
gW2a[0] = fmaf(d0, h1v, gW2a[0]);
gW2a[1] = fmaf(d1, h1v, gW2a[1]);
gW2a[2] = fmaf(d2, h1v, gW2a[2]);
gW2a[3] = fmaf(d3, h1v, gW2a[3]);
gW2b[0] = fmaf(d0, h2v, gW2b[0]);
gW2b[1] = fmaf(d1, h2v, gW2b[1]);
gW2b[2] = fmaf(d2, h2v, gW2b[2]);
gW2b[3] = fmaf(d3, h2v, gW2b[3]);
if (lane < 4) gb2 += dzT[t * 8 + lane];
gwva = fmaf(dvv, h1v, gwva);
gwvb = fmaf(dvv, h2v, gwvb);
if (lane == 0) gbv += dvv;
}
#pragma unroll 4
for (int j = 0; j < 64; ++j) {
float hj = hT[lane * 65 + j];
float4 w2 = w2q[j];
float dh = w2.x * dz0 + w2.y * dz1 + w2.z * dz2 + w2.w * dz3 + s_theta[580 + j] * dv;
hT[lane * 65 + j] = dh * (1.0f - hj * hj);
}
__syncwarp();
#pragma unroll
for (int t = 0; t < 32; ++t) {
float d1v = hT[t * 65 + j1];
float d2v = hT[t * 65 + j2];
float4 xv = ((const float4*)xT)[t];
gW1a[0] = fmaf(d1v, xv.x, gW1a[0]);
gW1a[1] = fmaf(d1v, xv.y, gW1a[1]);
gW1a[2] = fmaf(d1v, xv.z, gW1a[2]);
gW1a[3] = fmaf(d1v, xv.w, gW1a[3]);
gW1b[0] = fmaf(d2v, xv.x, gW1b[0]);
gW1b[1] = fmaf(d2v, xv.y, gW1b[1]);
gW1b[2] = fmaf(d2v, xv.z, gW1b[2]);
gW1b[3] = fmaf(d2v, xv.w, gW1b[3]);
gb1a += d1v; gb1b += d2v;
}
__syncwarp();
}
{
// reuse the (now consumed) h tile as this warp's private grad staging:
// plain smem stores, no atomic spin, then block-reduce the 4 copies
float* wg = hT;
#pragma unroll
for (int i = 0; i < 21; ++i) wg[lane + i * 32] = 0.0f;
__syncwarp();
wg[j1 * 4 + 0] = gW1a[0];
wg[j1 * 4 + 1] = gW1a[1];
wg[j1 * 4 + 2] = gW1a[2];
wg[j1 * 4 + 3] = gW1a[3];
wg[j2 * 4 + 0] = gW1b[0];
wg[j2 * 4 + 1] = gW1b[1];
wg[j2 * 4 + 2] = gW1b[2];
wg[j2 * 4 + 3] = gW1b[3];
wg[256 + j1] = gb1a;
wg[256 + j2] = gb1b;
wg[320 + j1] = gW2a[0];
wg[384 + j1] = gW2a[1];
wg[448 + j1] = gW2a[2];
wg[512 + j1] = gW2a[3];
wg[320 + j2] = gW2b[0];
wg[384 + j2] = gW2b[1];
wg[448 + j2] = gW2b[2];
wg[512 + j2] = gW2b[3];
wg[580 + j1] = gwva;
wg[580 + j2] = gwvb;
__syncwarp();
if (lane < 4) wg[576 + lane] = gb2;
if (lane == 0) wg[644] = gbv;
}
__syncthreads();
{
#pragma unroll
for (int i = 0; i < 6; ++i) {
int p = tid + i * NTHREADS;
if (p < NPARAM) {
float acc = s_hbase[0 * (32 * 65) + p];
#pragma unroll
for (int w = 1; w < NWARP; ++w) acc += s_hbase[w * (32 * 65) + p];
atomicAdd(&grad[gb * NPARAM + p], acc);
}
}
}
grid.sync();
// ---- post: redundant clipped-Adam in every block ----
float mygs[6];
float ssq = 0.0f;
#pragma unroll
for (int i = 0; i < 6; ++i) {
int p = tid + i * NTHREADS;
if (p < NPARAM) { mygs[i] = grad[gb * NPARAM + p]; ssq = fmaf(mygs[i], mygs[i], ssq); }
}
s_red[wib * 4 + 3] = ssq;
__syncthreads();
if (tid == 0) {
float acc = 0.0f;
for (int w = 0; w < NWARP; ++w) acc += s_red[w * 4 + 3];
s_scalar[2] = acc;
}
__syncthreads();
float total_norm = sqrtf(s_scalar[2]);
float clip_coef = MAX_GRAD_NORM / (total_norm + 1e-6f);
float cscale = clip_coef < 1.0f ? clip_coef : 1.0f;
float bc1 = (float)(1.0 - pow1);
float inv_bc2_sqrt = (float)(1.0 / sqrt(pow2 > 0.0 ? (1.0 - pow2) : 1.0));
float lr_t = LR / bc1;
#pragma unroll
for (int i = 0; i < 6; ++i) {
int p = tid + i * NTHREADS;
if (p < NPARAM) {
float gg = mygs[i] * cscale;
float mm = 0.9f * s_m[p] + 0.1f * gg;
float vv = 0.999f * s_v[p] + 0.001f * gg * gg;
s_m[p] = mm;
s_v[p] = vv;
float denom = sqrtf(vv) * inv_bc2_sqrt + 1e-8f;
s_theta[p] -= lr_t * mm / denom;
if (blockIdx.x == 0) theta_g[p] = s_theta[p];
}
}
pow1 *= 0.9;
pow2 *= 0.999;
__syncthreads();
for (int p = tid; p < 256; p += NTHREADS)
s_w2t[p] = s_theta[320 + (p & 3) * 64 + (p >> 2)];
const int zb = (gmb + 2) & 3;
#pragma unroll
for (int i = 0; i < 6; ++i) {
int p = tid + i * NTHREADS;
if (p < NPARAM) grad[zb * NPARAM + p] = 0.0f;
}
if (gmb == 15 && gtid < 2) ds[gtid] = 0.0;
}
}
}
}
void train_run(at::Tensor rec, at::Tensor grad,
at::Tensor theta, at::Tensor curve, at::Tensor dstats,
int64_t seed, int64_t iters) {
const int SMEM_FLOATS = 3 * NPARAM + 256 + 1 + NWARP * (32 * 65) + NWARP * (32 * 8) + NWARP * (32 * 4) + NWARP * 4 + 8;
const int SMEM_BYTES = SMEM_FLOATS * 4;
static int num_blocks = -1;
if (num_blocks < 0) {
int dev;
cudaGetDevice(&dev);
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, dev);
cudaError_t err = cudaFuncSetAttribute(train_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, SMEM_BYTES);
TORCH_CHECK(err == cudaSuccess, "smem attr: ", cudaGetErrorString(err));
int bpm = 0;
err = cudaOccupancyMaxActiveBlocksPerMultiprocessor(
&bpm, train_kernel, NTHREADS, SMEM_BYTES);
TORCH_CHECK(err == cudaSuccess, "occupancy: ", cudaGetErrorString(err));
TORCH_CHECK(bpm >= 1, "kernel not co-resident");
num_blocks = prop.multiProcessorCount * bpm;
}
float4* rec_p = reinterpret_cast<float4*>(rec.data_ptr<float>());
float* grad_p = grad.data_ptr<float>();
float* theta_p = theta.data_ptr<float>();
float* curve_p = curve.data_ptr<float>();
double* dstats_p = dstats.data_ptr<double>();
unsigned seed_lo = (unsigned)(seed & 0xffffffff);
unsigned seed_hi = (unsigned)((seed >> 32) & 0xffffffff) ^ 0x5EED5EEDu;
int iters_i = (int)iters;
void* args[] = {&rec_p,
&grad_p, &theta_p, &curve_p, &dstats_p, &seed_lo, &seed_hi, &iters_i};
cudaError_t err = cudaLaunchCooperativeKernel(
(void*)train_kernel, dim3(num_blocks), dim3(NTHREADS), args, SMEM_BYTES,
at::cuda::getCurrentCUDAStream());
TORCH_CHECK(err == cudaSuccess, "coop launch: ", cudaGetErrorString(err));
}
"""
def _get_ext():
global _EXT
if _EXT is None:
_EXT = load_inline(
name="grid_ppo_megakernel_v2",
cpp_sources=["void train_run(at::Tensor rec, at::Tensor grad, at::Tensor theta, at::Tensor curve, at::Tensor dstats, int64_t seed, int64_t iters);"],
cuda_sources=[_CUDA_SRC],
functions=["train_run"],
extra_cuda_cflags=[
"-O3",
"--use_fast_math",
"-gencode=arch=compute_120,code=sm_120",
],
verbose=False,
)
return _EXT
_BUFS = {}
def _get_bufs():
if _BUFS:
return _BUFS
dev = torch.device("cuda:0")
nsamp = 32 * 4096
_BUFS["rec"] = torch.zeros(nsamp * 8, device=dev, dtype=torch.float32)
_BUFS["grad"] = torch.zeros(4 * 645, device=dev, dtype=torch.float32)
_BUFS["theta"] = torch.zeros(645, device=dev, dtype=torch.float32)
_BUFS["curve"] = torch.zeros(1024, device=dev, dtype=torch.float32)
_BUFS["dstats"] = torch.zeros(4, device=dev, dtype=torch.float64)
return _BUFS
def train(total_env_steps: int, seed: int) -> list[float]:
ext = _get_ext()
b = _get_bufs()
rollout, num_envs = 32, 4096
iters = max(1, total_env_steps // (rollout * num_envs))
ext.train_run(
b["rec"], b["grad"], b["theta"], b["curve"], b["dstats"], int(seed), iters,
)
torch.cuda.synchronize()
c = b["curve"][:iters].cpu()
return (c / num_envs).tolist()
20260716_112743_kinetic-claude_kinetic-0715_01_rl_grid_ppo