kernels/rr: staged-image Swin layer engine prototypes

Per-layer staged-image engine for the eleven translated denoiser Swin
layers: descriptors (verified offsets per phase), cut recipes that splice an
isolated native phase into NVIDIA's PTX, wave templates, epilogue notes and
GPU probes. The isolated phase-8 splice produces a byte-identical final
4K frame but is slower than the translated body (14.90 vs 6.59 ms) - a
correctness experiment, not a shipping improvement; docs/rr-engine.md
records the design and the next direct-converter win (B operands built
from transposed accumulators).
This commit is contained in:
woutermans committed 2026-10-12 12:47:53 +02:00
1 parent 798e25611c
commit 0fe3a28cb7
58 files changed
+28330 -5

No files matched your search

+120
View File
@@ -0,0 +1,120 @@
// Layout probe: which k does each byte of the A (weight) and B (activation) operand hold?
//
// Method, with no assumption about byte order:
// * A (the prepared weight slot) row r holds a distinct e4m3 value per byte of its k16 row.
// * B (the staged activation) is an identity selector: column c is 1.0 at k = c, 0 elsewhere.
// * The WMMA computes D[r][c] = sum_k A[r][k] * B[k][c] = A[r][k(c)], so the value that
// comes back names the k that column c's byte was paired with. Both operands are filled
// byte-by-byte in memory order, so the printout is a direct read of the hardware's slot
// order for each side.
//
// Build and run:
// hipcc --offload-arch=gfx1201 -DD4R_TEX_FP8 -DD4R_WMMA_LAYOUT=12 -O2 \
// -o /tmp/probe kernels/tools/rrswin_operand_probe.cpp && /tmp/probe
#include <hip/hip_runtime.h>
#include <cstdio>
#include <cstdint>
#include <cmath>
#include <cstdlib>
#include <vector>
#include "../rr/rrswin_engine.h"
#define CHECK(expr) \
do \
{ \
hipError_t e_ = (expr); \
if (e_ != hipSuccess) \
{ \
std::printf("HIP error at %s:%d: %s -> %s\n", __FILE__, __LINE__, #expr, \
hipGetErrorString(e_)); \
std::exit(2); \
} \
} while (0)
static const int kRows = 16, kChunk = 16, kRowBytes = 32;
// 16 distinct, exactly representable e4m3 values indexed by k
static uint8_t value_for(int k)
{
const int e = 1 + (k & 7); // 1..8
const int m = (k >> 3) & 1; // 0 or 1
return (uint8_t)((e << 3) | (m ? 4 : 0));
}
static double dec(uint8_t c)
{
const int sign = c & 0x80, e = (c >> 3) & 0xf, m = c & 7;
const double mag = e == 0 ? (m / 8.0) * std::pow(2.0, -6.0) : (1.0 + m / 8.0) * std::pow(2.0, e - 7.0);
return sign ? -mag : mag;
}
__global__ void probe_kernel(const uint8_t* x_img, const uint8_t* w_prep, float* out)
{
__shared__ uint8_t img[kRows * kRowBytes];
for (int i = threadIdx.x; i < kRows * kRowBytes; i += blockDim.x)
img[i] = x_img[i];
__syncthreads();
f8v c = {0, 0, 0, 0, 0, 0, 0, 0};
c = rrswin::swin_gemm_tile(c, (const kslot*)w_prep, 0, img, kRowBytes, 0);
for (int i = 0; i < 8; ++i)
out[i * 32 + threadIdx.x] = c[i];
}
int main()
{
// A: weight row r, byte b of chunk 0 = value_for(b); the other chunk zero.
// B: activation column c = 1.0 at byte c of chunk 0, zero elsewhere.
const uint8_t one = 0x38; // e4m3 1.0
std::vector<uint8_t> w(32 * kChunk, 0), x(kRows * kRowBytes, 0);
// A row r carries only 2^(r-8) at byte 0, so the value read back names the row the
// engine actually selected; B column c is 1.0 at k = c.
// A row r = 1.0 at byte 0 only; B column c holds a distinct value per byte of its k16 row.
for (int r = 0; r < kRows; ++r)
w[(size_t)(r) * kChunk + 0] = one;
// B row r must be distinguishable by its byte 0 as well, or "which row" is invisible:
// byte b of row r carries value_for((b + r) % 16).
for (int c = 0; c < kRows; ++c)
for (int b = 0; b < 16; ++b)
x[(size_t)c * kRowBytes + b] = value_for((b + c) % 16);
uint8_t *dx, *dw;
float* dout;
CHECK(hipMalloc(&dx, x.size()));
CHECK(hipMalloc(&dw, w.size()));
CHECK(hipMalloc(&dout, 8 * 32 * sizeof(float)));
CHECK(hipMemcpy(dx, x.data(), x.size(), hipMemcpyHostToDevice));
CHECK(hipMemcpy(dw, w.data(), w.size(), hipMemcpyHostToDevice));
probe_kernel<<<1, 32>>>(dx, dw, dout);
CHECK(hipGetLastError());
CHECK(hipDeviceSynchronize());
std::vector<float> got(8 * 32);
CHECK(hipMemcpy(got.data(), dout, got.size() * sizeof(float), hipMemcpyDeviceToHost));
// The engine's assumed indexing: value at D[row = i + 8h][col = lane & 15].
// A is the identity at byte 0, so D(row, col) = B[k0][col] = value_for((k0 + row(col)) % 16).
// Print the value_for index of every (vgpr, lane) so the true (row, column) assignment can
// be read off the table instead of assumed.
std::printf("value_for index at got[vgpr i][lane l] (rows = l 0..31, cols = i 0..7):\n");
std::printf(" ");
for (int i = 0; i < 8; ++i) std::printf(" i%-3d", i);
std::printf("\n");
for (int l = 0; l < 32; ++l)
{
std::printf("l%-4d ", l);
for (int i = 0; i < 8; ++i)
{
const double v = got[i * 32 + l];
int n = -1;
for (int q = 0; q < 16; ++q)
if (std::fabs(v - dec(value_for(q))) < 1e-3) n = q;
if (n < 0) std::printf(" ? ");
else std::printf(" %-3d ", n);
}
std::printf("\n");
}
CHECK(hipFree(dx));
CHECK(hipFree(dw));
CHECK(hipFree(dout));
return 0;
}