From 12e059c6abcb2aa00ab5ca108a403e259f2e6c9c Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Thu, 21 May 2026 01:06:54 +0200 Subject: [PATCH] feat(gpu-log): add gpu_log_ring.cu device helpers + tick kernel --- crates/ml-alpha/build.rs | 3 +- crates/ml-alpha/cuda/gpu_log_ring.cu | 88 ++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 crates/ml-alpha/cuda/gpu_log_ring.cu diff --git a/crates/ml-alpha/build.rs b/crates/ml-alpha/build.rs index 605b09de7..5572050ac 100644 --- a/crates/ml-alpha/build.rs +++ b/crates/ml-alpha/build.rs @@ -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"); diff --git a/crates/ml-alpha/cuda/gpu_log_ring.cu b/crates/ml-alpha/cuda/gpu_log_ring.cu new file mode 100644 index 000000000..3c5c23846 --- /dev/null +++ b/crates/ml-alpha/cuda/gpu_log_ring.cu @@ -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 + +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. +}