feat(gpu-log): add gpu_log_ring.cu device helpers + tick kernel

This commit is contained in:
jgrusewski
2026-05-21 01:06:54 +02:00
parent cd94a34122
commit 12e059c6ab
2 changed files with 90 additions and 1 deletions

View File

@@ -23,9 +23,10 @@ const KERNELS: &[&str] = &[
"reduce_axis0", // Phase B: cross-batch param-grad reducer
"output_smoothness", // CRT.train: per-horizon adjacent-position prob-jitter penalty
"smoothness_lambda_controller", // CRT.train: ISV-driven λ controller anchored on h30 jitter
"gpu_log_ring", // GPU diagnostic log ring — tick kernel + log_record helper
];
// Cache bust v13 (2026-05-21): smoothness_lambda_controller.cu added — ISV-driven λ.
// Cache bust v14 (2026-05-21): gpu_log_ring.cu added — unified in-kernel diagnostic logging.
fn main() {
println!("cargo:rerun-if-changed=build.rs");

View File

@@ -0,0 +1,88 @@
// gpu_log_ring.cu — unified GPU diagnostic log ring (device-side).
//
// Provides `log_record()` (a device __forceinline__ helper) and the
// `gpu_log_tick` kernel that increments the step counter once per
// captured-graph step.
//
// Constraints:
// - No atomicAdd (slot reservation by deterministic step/kid/rt math).
// - No host branches; thread-id gating only.
// - Header-last commit (magic field written after payload + other header).
// - Mapped-pinned ring; host reads via volatile, no DtoH memcpy.
//
// Per `feedback_nvidia_grade_perf_for_kernels`: single-thread writer for
// the slot's 64 B cacheline, one coalesced store.
#include "gpu_log_ids.h"
#include <cstdint>
struct LogHeader {
uint32_t magic;
uint32_t step;
uint8_t kernel_id;
uint8_t record_type;
uint8_t payload_words;
uint8_t reserved;
uint32_t _padding;
};
struct LogRecord {
LogHeader header;
float payload[GPU_LOG_PAYLOAD_F32];
};
struct LogRing {
LogRecord records[GPU_LOG_N_SLOTS];
};
// Tick kernel — 1 thread, 1 block. Increments step counter; runs FIRST in
// every captured-graph replay so all subsequent kernels see the new step.
extern "C" __global__ void gpu_log_tick(int* step_counter) {
if (threadIdx.x == 0 && blockIdx.x == 0) {
step_counter[0] = step_counter[0] + 1;
}
}
// log_record — emit a diagnostic record from a kernel.
//
// Single-writer per (step, kernel_id, record_type) slot. Thread (0,0) of
// block 0 writes; all other threads no-op. Header-last commit ensures
// torn-record reads on the host side are detectable via magic mismatch.
//
// Caller responsibility:
// - g_log_ring == nullptr → no-op (logging disabled).
// - g_step_counter is the device step counter (incremented by gpu_log_tick).
// - payload_words ≤ GPU_LOG_PAYLOAD_F32 (12); kernel must respect this.
__device__ __forceinline__ void log_record(
LogRing* g_log_ring,
const int* g_step_counter,
uint8_t kernel_id,
uint8_t record_type,
const float* payload,
int payload_words
) {
if (g_log_ring == nullptr) return;
if (blockIdx.x != 0 || threadIdx.x != 0) return;
const int step = g_step_counter[0];
const int slot = (step * GPU_LOG_N_RECORDS_PER_STEP
+ (int)kernel_id * GPU_LOG_N_RT_PER_KERNEL
+ (int)record_type) & (GPU_LOG_N_SLOTS - 1);
LogRecord* rec = &g_log_ring->records[slot];
const int n = payload_words < GPU_LOG_PAYLOAD_F32 ? payload_words : GPU_LOG_PAYLOAD_F32;
#pragma unroll
for (int i = 0; i < GPU_LOG_PAYLOAD_F32; ++i) {
rec->payload[i] = (i < n) ? payload[i] : 0.0f;
}
rec->header.step = (uint32_t)step;
rec->header.kernel_id = kernel_id;
rec->header.record_type = record_type;
rec->header.payload_words = (uint8_t)n;
rec->header.reserved = 0;
rec->header._padding = 0;
__threadfence(); // make payload + header fields visible …
rec->header.magic = GPU_LOG_MAGIC; // … BEFORE the commit sentinel.
}