Atomic refactor wiring the GPU log ring's first producer end-to-end: - cuda/gpu_log_helpers.cuh (new): extract LogHeader/LogRecord/LogRing struct definitions + the log_record() device __forceinline__ helper into a shared header. Single source of truth — other kernels include this rather than redefining the structs. - cuda/gpu_log_ring.cu: refactor to include gpu_log_helpers.cuh; retain only the gpu_log_tick kernel. - cuda/smoothness_lambda_controller.cu: include gpu_log_helpers.cuh, add trailing (LogRing*, const int*) args, capture pre-EMA jitter + post-EMA jitter + target + excess_ratio into shared mem, and emit three records per call (RT_INPUT, RT_STATE, RT_OUTPUT) gated on non-null ring pointer. - trainer/perception.rs: feature-gated (cuda-diag-log) ring allocation + step counter (mapped-pinned host shadow), gpu_log_tick launch FIRST inside the captured graph, two new pointer args on the smoothness controller launch (null when feature off), step-counter DtoD shadow alongside the other telemetry shadows, drain task spawn (skipped when no tokio runtime — sync tests still construct the trainer), and a feature-gated Drop impl that aborts the drain task on shutdown. - tests/smoothness_lambda_controller_invariants.rs: pass null pointers for the two new kernel args; the kernel's nullptr guard preserves pre-existing behaviour. - build.rs: rerun-if-changed on cuda/gpu_log_ids.h and cuda/gpu_log_helpers.cuh so header edits trigger cubin rebuilds. Verified: cargo build / check --all-targets clean both with and without the cuda-diag-log feature; 4/4 smoothness controller invariant tests pass; 9/9 perception_overfit integration tests pass under the feature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
81 lines
2.7 KiB
Plaintext
81 lines
2.7 KiB
Plaintext
// gpu_log_helpers.cuh — header-only device helpers for the GPU log ring.
|
|
//
|
|
// Include this from any .cu file that needs to call `log_record()`.
|
|
// The `gpu_log_tick` kernel itself lives in `gpu_log_ring.cu`.
|
|
//
|
|
// Per `feedback_single_source_of_truth_no_duplicates`: this is the ONE
|
|
// definition of `LogHeader` / `LogRecord` / `LogRing` and `log_record`;
|
|
// other kernels must include this header instead of duplicating the
|
|
// struct layouts. The IDs (kernel_id / record_type / magic / slot math
|
|
// constants) live in `gpu_log_ids.h` and are included here.
|
|
|
|
#ifndef GPU_LOG_HELPERS_CUH
|
|
#define GPU_LOG_HELPERS_CUH
|
|
|
|
#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];
|
|
};
|
|
|
|
// 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.
|
|
}
|
|
|
|
#endif // GPU_LOG_HELPERS_CUH
|