|
|
|
|
@@ -316,28 +316,28 @@ pub struct PerceptionTrainer {
|
|
|
|
|
/// Cached handle for `smoothness_lambda_controller`.
|
|
|
|
|
smoothness_controller_fn: CudaFunction,
|
|
|
|
|
_smoothness_controller_module: Arc<CudaModule>,
|
|
|
|
|
// ── GPU log ring (feature: cuda-diag-log) ──
|
|
|
|
|
// ── GPU log ring (feature: kernel-step-trace) ──
|
|
|
|
|
// When enabled, the trainer allocates a mapped-pinned ring + a device
|
|
|
|
|
// step counter, passes their pointers into logging-capable kernels
|
|
|
|
|
// (currently `smoothness_lambda_controller`), and spawns a tokio drain
|
|
|
|
|
// task that polls the ring at 500 ms cadence and emits structured
|
|
|
|
|
// tracing events. See `docs/superpowers/specs/2026-05-21-gpu-log-ring-design.md`.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_ring: std::sync::Arc<crate::gpu_log::LogRing>,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_step_counter_d: CudaSlice<i32>,
|
|
|
|
|
/// Mapped-pinned host shadow of `log_step_counter_d`. Updated via
|
|
|
|
|
/// memcpy_dtod_async inside the captured region; read by the drain
|
|
|
|
|
/// task each tick.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_step_counter_host_shadow: std::sync::Arc<crate::pinned_mem::MappedRecordBuffer<i32>>,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
gpu_log_tick_fn: CudaFunction,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
_gpu_log_module: Arc<CudaModule>,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_drain_stop: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_drain_handle: Option<tokio::task::JoinHandle<()>>,
|
|
|
|
|
// X6: LN_b weights (formerly ln_gain_d / ln_bias_d) moved to
|
|
|
|
|
// `self.trunk.ln_b_gain_d` / `ln_b_bias_d`. LN_b is the LayerNorm
|
|
|
|
|
@@ -1129,24 +1129,24 @@ impl PerceptionTrainer {
|
|
|
|
|
let smoothness_jitter_first_obs_d = stream.alloc_zeros::<i32>(1)
|
|
|
|
|
.context("smoothness_jitter_first_obs_d alloc")?;
|
|
|
|
|
|
|
|
|
|
// ── GPU log ring allocation (feature: cuda-diag-log) ──
|
|
|
|
|
// ── GPU log ring allocation (feature: kernel-step-trace) ──
|
|
|
|
|
// Per `feedback_no_htod_htoh_only_mapped_pinned`: ring is mapped-
|
|
|
|
|
// pinned; step counter shadow is mapped-pinned; the on-stream
|
|
|
|
|
// DtoD memcpy of the device step counter into the host shadow
|
|
|
|
|
// is captured in the graph, so the drainer sees the latest
|
|
|
|
|
// counter without an extra sync.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_ring = std::sync::Arc::new(crate::gpu_log::LogRing::alloc()?);
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_step_counter_d = crate::gpu_log::alloc_step_counter(&stream)?;
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_step_counter_host_shadow = {
|
|
|
|
|
let buf = unsafe { crate::pinned_mem::MappedRecordBuffer::<i32>::new(1) }
|
|
|
|
|
.map_err(|e| anyhow::anyhow!("log step counter shadow: {e}"))?;
|
|
|
|
|
buf.write_record(0, 0);
|
|
|
|
|
std::sync::Arc::new(buf)
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let (gpu_log_tick_fn, gpu_log_module) = {
|
|
|
|
|
const GPU_LOG_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/gpu_log_ring.cubin"));
|
|
|
|
|
let module = ctx.load_cubin(GPU_LOG_CUBIN.to_vec())
|
|
|
|
|
@@ -1154,7 +1154,7 @@ impl PerceptionTrainer {
|
|
|
|
|
let f = module.load_function("gpu_log_tick").context("load gpu_log_tick")?;
|
|
|
|
|
(f, module)
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_drain_stop = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
|
|
|
|
// The drain task uses `tokio::spawn`, which requires a runtime
|
|
|
|
|
// handle on the current thread. In production the trainer runs
|
|
|
|
|
@@ -1163,7 +1163,7 @@ impl PerceptionTrainer {
|
|
|
|
|
// (records still land in the ring; the test simply doesn't read
|
|
|
|
|
// them through the structured tracing path). This is the
|
|
|
|
|
// canonical pattern for opt-in async telemetry.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_drain_handle = if tokio::runtime::Handle::try_current().is_ok() {
|
|
|
|
|
Some(crate::gpu_log::spawn_drain_task(
|
|
|
|
|
log_ring.clone(),
|
|
|
|
|
@@ -1198,19 +1198,19 @@ impl PerceptionTrainer {
|
|
|
|
|
smoothness_jitter_first_obs_d,
|
|
|
|
|
smoothness_controller_fn,
|
|
|
|
|
_smoothness_controller_module: smoothness_controller_module,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_ring,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_step_counter_d,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_step_counter_host_shadow,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
gpu_log_tick_fn,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
_gpu_log_module: gpu_log_module,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_drain_stop,
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
log_drain_handle,
|
|
|
|
|
// LayerNorm state. `ln_gain_d` / `ln_bias_d` constructed
|
|
|
|
|
// above via `upload` (gain=1, bias=0). LN_HIDDEN matches
|
|
|
|
|
@@ -1668,13 +1668,13 @@ impl PerceptionTrainer {
|
|
|
|
|
k_seq: usize,
|
|
|
|
|
total_snaps: usize,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
// ── GPU log ring tick (feature: cuda-diag-log) ──
|
|
|
|
|
// ── GPU log ring tick (feature: kernel-step-trace) ──
|
|
|
|
|
// Runs FIRST inside the captured graph so every downstream
|
|
|
|
|
// logging-capable kernel sees the new step counter. Single-
|
|
|
|
|
// thread/single-block kernel: no host malloc, no host branch
|
|
|
|
|
// (per `pearl_cudarc_disable_event_tracking_for_graph_capture`
|
|
|
|
|
// and `pearl_no_host_branches_in_captured_graph`).
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
{
|
|
|
|
|
let tick_cfg = LaunchConfig {
|
|
|
|
|
grid_dim: (1, 1, 1),
|
|
|
|
|
@@ -2157,16 +2157,16 @@ impl PerceptionTrainer {
|
|
|
|
|
// GPU log ring pointers. With feature off, both are 0 (null) and
|
|
|
|
|
// the kernel's `log_record()` short-circuits at the nullptr guard
|
|
|
|
|
// — behaviorally identical to the pre-log kernel.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_ring_ptr: cudarc::driver::sys::CUdeviceptr = self.log_ring.dev_ptr();
|
|
|
|
|
#[cfg(not(feature = "cuda-diag-log"))]
|
|
|
|
|
#[cfg(not(feature = "kernel-step-trace"))]
|
|
|
|
|
let log_ring_ptr: cudarc::driver::sys::CUdeviceptr = 0;
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
let log_step_ptr: cudarc::driver::sys::CUdeviceptr = {
|
|
|
|
|
let (p, _g) = self.log_step_counter_d.device_ptr(&self.stream);
|
|
|
|
|
p
|
|
|
|
|
};
|
|
|
|
|
#[cfg(not(feature = "cuda-diag-log"))]
|
|
|
|
|
#[cfg(not(feature = "kernel-step-trace"))]
|
|
|
|
|
let log_step_ptr: cudarc::driver::sys::CUdeviceptr = 0;
|
|
|
|
|
{
|
|
|
|
|
let mut launch = self.stream.launch_builder(&self.smoothness_controller_fn);
|
|
|
|
|
@@ -2755,7 +2755,7 @@ impl PerceptionTrainer {
|
|
|
|
|
// how many steps have completed since its last drain. Captured
|
|
|
|
|
// inside the same graph as the other shadow memcpys, so a single
|
|
|
|
|
// end-of-step sync makes all of them visible.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
unsafe {
|
|
|
|
|
let (src_ptr, _g) = self.log_step_counter_d.device_ptr(&self.stream);
|
|
|
|
|
cudarc::driver::result::memcpy_dtod_async(
|
|
|
|
|
@@ -4179,7 +4179,7 @@ fn download(stream: &Arc<CudaStream>, src: &CudaSlice<f32>) -> Result<Vec<f32>>
|
|
|
|
|
// runtime / hanging shutdown. Drop cannot be async, so we cannot await
|
|
|
|
|
// the handle — abort is the canonical pattern for tokio JoinHandle in
|
|
|
|
|
// non-async destructors.
|
|
|
|
|
#[cfg(feature = "cuda-diag-log")]
|
|
|
|
|
#[cfg(feature = "kernel-step-trace")]
|
|
|
|
|
impl Drop for PerceptionTrainer {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
|
|