10 Commits

Author SHA1 Message Date
jgrusewski
f1093e9a07 perf(cudarc): kill stream sync management — always returns false
Force cudarc's is_managing_stream_synchronization() to false.
Eliminates ALL internal event tracking (cuStreamSynchronize,
cuEventRecord, cuStreamWaitEvent) that was adding 18.7 syncs/step.

All stream synchronization is managed manually via raw_launch events.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:13:25 +02:00
jgrusewski
3be864d9f9 feat(rl): raw CUDA launch wrapper + cudarc cu_function() accessor
raw_launch.rs: RawArgs fixed-array u64 storage + raw_launch() calling
cuLaunchKernel directly. Bypasses PushKernelArg trait dispatch, Arc
bookkeeping, event guards. Foundation for 500+ sps hot path.

vendor/cudarc: added CudaFunction::cu_function() accessor mirroring
CudaStream::cu_stream() pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:45:57 +02:00
jgrusewski
0f8395450f feat: child graph architecture — 5 composable sub-graphs in single parent launch
Replaces 6 separate CUDA graphs (graph_forward, graph_forward_ddqn,
graph_adam, graph_aux, graph_spectral, graph_mega) with composable
child sub-graphs assembled into a single parent graph.

Architecture:
  parent_graph (single cuGraphLaunch per step)
    ├── spectral_child (spectral norm)
    ├── forward_child (cuBLAS trunk + temporal + ISV + loss + backward)
    ├── ddqn_child (Double DQN Pass 3)
    ├── aux_child (HER + EMA + IQL + IQN + attention + CQL)
    └── adam_child (mamba2 bwd + pruning + grad_norm + adam + ISV update)

Each child captured independently via cudarc begin/end_capture,
composed via cudaGraphAddChildGraphNode with sequential dependencies.
Only parent is instantiated and launched.

Removed: SendSyncGraph, RawCudaGraph, capture_training_graphs,
replay_forward, replay_adam, aux_frequency, all old graph fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:40:10 +02:00
jgrusewski
c8df643e67 feat: graph experience collector forward to prevent cuBLAS state contamination
Capture the experience collector's bottleneck + Q-network forward pass
into a CUDA Graph on first timestep. Prevents ungraphed cublasLtMatmul
calls from contaminating cuBLAS internal state between epochs.

Root cause identified: multiple cuBLAS handles on the same CUDA stream.
NVIDIA docs: "maintain a separate cuBLAS handle for each stream" —
implying one handle per stream. We have 3 handles (experience collector,
training, DDQN) on one stream.

Proper fix (next session): refactor CublasForward to share a single
cuBLAS handle across all components. Separate the handle (shared)
from per-component cached descriptors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:21:14 +02:00
jgrusewski
bfd88a7e12 feat: replay graph_mega for eval with param save/restore
Route evaluation Q-values through graph_mega (the EXACT graph used for
training). Save params before replay, restore after to undo Adam's
weight update. Persist graph_mega and graph_forward across fold
boundaries — all buffer addresses are stable, kernel args use pinned
device-mapped pointers.

This guarantees training and eval use identical cublasLtMatmul kernel
configs from the same capture session. Both are fully deterministic
WITHIN a given process. Cross-process variation remains from the
initial graph capture (NVIDIA cublasLtMatmul heuristic selects
different kernel configs between process invocations).

Cleanup: removed separate eval_fwd_graph, eval_params_snapshot serves
dual purpose (fold boundary save + eval rollback).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 10:07:16 +02:00
jgrusewski
8f8d230588 feat: frozen eval graph — replay first-capture forward for deterministic eval
Capture a separate eval-only CUDA Graph immediately after graph_forward
(same cublasLtMatmul internal state). The exec handle is leaked via
mem::forget to survive graph_forward invalidation between folds.

Eval replays this frozen graph instead of the recaptured training
graph_forward, giving consistent results across walk-forward folds.

Training: fully bit-identical (verified with weight checksums).
Evaluation: identical epochs 1-4 across runs. Remaining minor
variation from cublasLtMatmul internal state differences between
consecutive capture sessions — NVIDIA library limitation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:44:59 +02:00
jgrusewski
0fa5b993dd fix(proper): force cuMemAlloc_v2 in cudarc — cuMemAllocAsync incompatible with cublasLtMatmul on H100
Root cause: cuMemAllocAsync (stream-ordered memory pools) produces
memory that cublasLtMatmul rejects with CUBLAS_STATUS_NOT_SUPPORTED
on H100 (CUDA 13.0 driver) when the matmul runs on a different
stream than the allocation. Despite CUDA documentation stating
same-device async allocations should be accessible from all streams
with proper event synchronization, cublasLtMatmul disagrees.

Proof chain:
- C++ test with cudaMalloc: ALL dimensions pass on H100 ✓
- Rust test with cuMemAlloc: ALL dimensions pass on H100 ✓
- Rust test with cudarc alloc_zeros (cuMemAllocAsync): FAILS ✗
- Same dimensions, same handle, same workspace — only alloc differs

Fix: set has_async_alloc=false unconditionally in CudaContext::new().
This forces cuMemAlloc_v2 for ALL allocations. Zero performance impact
(allocations at construction time, not in hot loops).

Also: converted has_async_alloc from bool to AtomicBool for safety.
Removed diagnostic code (CUBLASLT_DIAG, test_lt_matmul_raw).
Reverted cuMemAlloc workspace hack (now uses normal alloc_zeros).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:33:50 +02:00
jgrusewski
5a72d6e00b fix(critical): disable cuMemAllocAsync — use cuMemAlloc for cublasLtMatmul compat
Root cause: cudarc uses cuMemAllocAsync (stream-ordered memory pools)
on H100 where CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED=1. These
allocations are only accessible from the allocating stream.
cublasLtMatmul dispatches GEMM work on branch streams (multi-stream
fork-join for parallel advantage heads), which can't access
stream-local memory from the main stream — returns NOT_SUPPORTED.

Proof: C++ test with cudaMalloc passes ALL dims on H100 ✓
       Rust test_lt_matmul_raw with cuMemAlloc passes on H100 ✓
       Same Rust code with cudarc alloc_zeros (cuMemAllocAsync) FAILS ✗

Fix: add CudaContext::disable_async_alloc() to vendored cudarc.
Called in DQN::new_with_stream() to force cuMemAlloc_v2 for ALL
allocations. No performance impact (allocations at construction,
not in hot loops).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:42:04 +02:00
jgrusewski
316233372a refactor(bf16): CudaSlice::raw_ptr() — eliminate ALL device_ptr() calls
Added raw_ptr() method to vendor/cudarc CudaSlice — returns device
address without event tracking (no SyncOnDrop guard leak).

Replaced ALL 119 raw_device_ptr() wrapper calls with .raw_ptr().
Deleted raw_device_ptr, raw_device_ptr_i32, raw_device_ptr_u32 wrappers.
Added forward_online_raw / forward_target_raw for graph-safe forward.
All CachedPtrs used in graph-captured code paths.

Smoke test: model trains (Sharpe improves -24→+1), but Q-stats
readback still returns 1e30. The issue is NOT device_ptr event
tracking — deeper investigation needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 17:13:31 +01:00
jgrusewski
07d0e60fe4 feat(bf16): remove nvrtc from entire workspace + wire ml-core precompiled cubins
- Fork cudarc locally (vendor/cudarc): add CudaContext::load_cubin()
  that calls cuModuleLoadData directly — zero nvrtc dependency
- Remove "nvrtc" feature from ml-core, ml-dqn, ml-ppo Cargo.toml
- Replace all 89 Ptx::from_binary + load_module calls with load_cubin
- ml-core cuda_autograd: wire 9 stub constructors to precompiled cubins
  (activation, elementwise, linear, loss, reduction, dropout, layer_norm, optimizer)
- ml-core build.rs: compile 8 BF16-native CUDA kernels via nvcc
- cubin_loader.rs: thin wrapper around CudaContext::load_cubin()
- Fix size_of::<f32> in gpu_tensor.rs, stream_ops.rs, layer_norm.rs
- Fix test data: Vec<f32> → Vec<half::bf16> for memcpy_htod
- Stub ml-ppo/ml-dqn runtime compile_ptx calls (dead code)
- backtest_metrics_kernel.cu: full native BF16 rewrite (no float)
- backtest_env_kernel.cu: shared memory → __nv_bfloat16

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 10:11:46 +01:00