Commit Graph

4015 Commits

Author SHA1 Message Date
jgrusewski
3a2923b800 feat(ci): per-binary selective compilation in Argo pipeline
Replace monolithic compile-all with granular per-binary change detection.
detect-changes now outputs space-separated package/example lists based on
a dependency map from source directories to binary targets:

  - Shared crates (common, config, Cargo.toml) → all binaries
  - Service-specific dirs → only that service binary
  - Domain crates (trading_engine, risk) → dependent service subset
  - ML crates → ml-training-service + trading-service + all training
  - ML subdirs (trainers/, hyperopt/, evaluation/) → specific training binaries

compile-services and compile-training accept package lists and build only
affected binaries, saving ~20-30s link time per skipped binary.

deploy-services restarts only affected deployments (trading-service
excluded from auto-deploy for safety).

Fix: 'latest' package update now replaces individual files instead of
deleting the entire package, preventing corruption during partial builds.

compile-and-train-template: derive needed training binaries from model
parameter (3 instead of 7), drop unused training_uploader build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:32:07 +01:00
jgrusewski
403725e4ce perf(cuda): replace memcpy_dtoh with mapped pinned memory in training guard
Eliminate all per-step GPU→CPU synchronization barriers from the
training guard. Replace device+host buffer pairs and memcpy_dtoh
with cuMemHostAlloc(DEVICEMAP) mapped pinned memory.

Key changes:
- MappedBuffer struct: cuMemHostAlloc + cuMemHostGetDevicePointer_v2
  allocates memory visible to both CPU and GPU simultaneously
- Double-buffering: kernel writes to buffer[N%2], CPU reads buffer
  [(N-1)%2] — one-step delayed halt detection, zero sync
- __threadfence_system() in CUDA kernels ensures writes visible to
  CPU across PCIe without explicit memcpy
- read_volatile on host pointer prevents CPU-side caching

Eliminated:
- check_and_accumulate: 28-byte memcpy_dtoh (every training step)
- qvalue_stats: 16-byte memcpy_dtoh (every 50 steps)
- qvalue_divergence: 20-byte memcpy_dtoh (every 50 steps)

Kept: read_accumulators memcpy_dtoh (12 bytes, epoch boundary only —
accumulator buffer stays in device memory for kernel read-modify-write).

1286 tests pass (878 ml + 408 ml-dqn), 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:51:59 +01:00
jgrusewski
607541aaef perf(dqn): eliminate remaining GPU→CPU readbacks from CUDA guard hot path
Two remaining GPU→CPU synchronization barriers in the CUDA-active
training loop:

1. detect_dead_neurons() → to_vec0() called every training step from
   log_diagnostics() in the guard path. Added check_gradient_collapse()
   method that performs the same collapse detection logic but skips the
   expensive per-parameter weight scan. Dead neuron detection now only
   runs at epoch boundary via log_diagnostics().

2. forward() Q-value clipping monitoring → to_vec2() called every 1000
   steps during compute_loss_internal() and Q-value estimation forward
   passes. Added training_forward_active flag that gates the monitoring
   block; set to true during all training-path forward() calls
   (compute_loss_internal + Q-value estimation), false during
   inference/evaluation.

All 1577 tests pass (878 ml + 408 ml-dqn + 291 ml-core), 0 failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:51:59 +01:00
jgrusewski
3dde6d285c feat(cuda): GPU-resident action routing in select_actions_batch_gpu
Wire route_exposure_to_factored() into both select_actions_batch_gpu
and select_actions_batch GPU paths, eliminating per-item CPU routing.
Previously, exposure indices (0-4) were downloaded from GPU and routed
to factored indices (0-44) one-by-one on CPU via route_action(). Now
the exposure→factored mapping runs entirely on GPU via the routing
kernel, with a single batch readback of the final factored indices.

Branching DQN path unchanged (already produces factored indices 0-44).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
ce534c26a5 perf(dqn): wire GPU Q-value monitoring — eliminate to_scalar/to_vec2 readbacks
Replace CPU-bound Q-value monitoring with GPU-resident qvalue_stats and
qvalue_divergence kernels from GpuTrainingGuard. The two readback sites
(estimate_avg_q_value_with_early_stopping's mean_all().to_scalar() and
log_q_values' to_vec2()) are now bypassed when the GPU training guard is
active. CPU fallback path preserved for non-CUDA builds and guard-absent
scenarios.

Changes:
- Add DQN::log_q_values_from_stats() accepting pre-computed GPU stats
- Add DQNAgentType::log_q_values_from_stats() delegate
- Replace Q-estimation in train_step_single_batch with GPU kernel path
- Replace Q-estimation in train_step_with_accumulation with GPU kernel path
- Import IndexOp trait for Tensor::i() in trainer.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
ed91f12dd2 feat(cuda): add batch_route_exposure_to_factored kernel for GPU-resident action routing
Adds a standalone CUDA kernel that converts DQN exposure indices (0-4)
to factored action indices (0-44) entirely on GPU, reusing the existing
route_order() device function from common_device_functions.cuh. Wired
into GpuActionSelector as route_exposure_to_factored() method, following
the same DtoD copy pattern as the existing select_actions methods. This
eliminates a GPU->CPU->GPU roundtrip when post-hoc routing is needed
after epsilon_greedy_select.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
56a7a2406b feat(cuda): bypass check_gradients_finite when GPU training guard active
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
6bbd2eb5ce feat(cuda): wire GpuTrainingGuard into train_step_with_accumulation (zero-sync accumulators)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
38f0bdec00 perf(dqn): wire GpuTrainingGuard into train_step_single_batch (zero-sync loss/grad)
Replace the GPU->CPU sync barrier (to_vec1 readback) in the DQN training
hot path with the GpuTrainingGuard CUDA kernel that performs NaN detection,
loss clipping, and gradient collapse checks entirely on-device. The guard
is lazy-initialized on first training step and falls back to the original
CPU readback path if CUDA kernel compilation fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
0aaa53bd18 test(cuda): add training guard CPU parity tests
Seven pure-Rust reference tests covering all four training_guard_kernel.cu
kernels: PTX NVRTC smoke-test (skips on CPU-only), NaN/Inf detection,
loss clip, gradient collapse, accumulator averaging, GuardResult boolean
threshold construction, and qvalue_stats_reduce per-sample max-Q reduction.
Zero clippy warnings in the new file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
966300aa49 feat(cuda): add GpuTrainingGuard wrapper with pinned memory + accumulators
Wraps 4 CUDA kernels (training_guard_check, training_guard_accumulate,
qvalue_stats_reduce, qvalue_divergence_check) with a Rust struct that
uses OnceLock PTX caching, pre-allocated device buffers, and host-side
Vec mirrors for zero-allocation readbacks per training step.
Accumulator (3-float acc_buf) stays on-device for epoch-boundary
averaging without CPU roundtrips.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
fb1fac3e93 feat(cuda): add training guard + Q-value monitor CUDA kernels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
7b412d328b docs: add zero-CPU DQN training hot path implementation plan
11-task plan across 4 chunks: Training Guard kernel + wrapper (Tasks 1-3),
wire into trainer (Tasks 4-6), Q-value monitor + action routing (Tasks 7-9),
experience collector audit + final verification (Tasks 10-11).

Eliminates all 10 GPU→CPU sync barriers from the DQN training hot path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
41440e9ae7 docs: add zero-CPU DQN training hot path design spec
Defines architecture for eliminating all 10 GPU→CPU sync barriers from
the training loop via 4 new GPU components: Training Guard (pinned
memory predicates), Q-Value Monitor (on-device accumulator), GPU-resident
action selection, and async experience collector readback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:49:40 +01:00
jgrusewski
0c385c38e2 Merge branch 'worktree-gpu-hotpath-audit' into main
GPU hotpath optimization: zero GPU→CPU roundtrips during training.
Adds DSR/n-step config, branching DQN GPU action selection,
warp experience kernel, GPU statistics, and epsilon-greedy kernel.

Merge conflicts resolved in 3 files:
- dqn.rs: kept clippy fixes + branch structure
- gpu_experience_collector.rs: removed duplicate methods, kept hotpath version
- trainer.rs: fixed brace mismatch from conflict resolution

Applied clippy fixes to new hotpath files (wildcard_enum_match_arm,
missing_debug_implementations).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 14:00:45 +01:00
jgrusewski
48b05fff89 fix(clippy): zero warnings across entire workspace
Merge clippy cleanup branch (5 commits, 198 files):
- cargo clippy --fix auto-fixes (150 files)
- Doc backticks, const fn, Display traits (89 files)
- write!() over format_push_string, iterator patterns (48 files)
- ML-crate pedantic allows following existing pattern
- Result: cargo clippy --workspace -- -D warnings = 0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:28:33 +01:00
jgrusewski
d06c99b0e1 fix(clippy): achieve zero warnings across entire workspace
- Fix format_push_string: write!() instead of push_str(&format!()) (25 sites)
- Fix str_to_string: .to_owned() instead of .to_string() on &str (6 sites)
- Fix unseparated_literal_suffix: add _ separator (6 sites)
- Fix multiple_inherent_impl: merge split impl blocks in TGGN, TFT, OFI (3)
- Fix else_if_without_else: add exhaustive else clauses (3 sites)
- Fix if_then_some_else_none: use .then().transpose() (1 site)
- Fix unwrap_in_result: replace expect() with match + ? (2 sites)
- Fix wildcard_enum_match_arm: enumerate Storage variants explicitly (2)
- Fix decimal_literal_representation: use hex for power-of-2 constants (5)
- Fix rc_buffer: Arc<Vec<T>> → Arc<[T]> for OFI features
- Fix needless_range_loop: convert to iterator patterns (17 sites)
- Fix used_underscore_binding: remove prefix on used vars (6 sites)
- Fix doc list item indentation (7 sites)
- Allow too_many_arguments on ML training functions (4)
- Allow multiple_unsafe_ops_per_block on CUDA FFI functions (3)
- Allow upper_case_acronyms on SLSTM/MLSTM model names (2)
- Add ML-crate pedantic allows: shadow, similar_names, type_complexity,
  indexing_slicing, partial_pub_fields, non_ascii_literal, same_name_method
  (following existing ml-labeling/ml-universe pattern)

Result: cargo clippy --workspace -- -D warnings passes with zero warnings.
All 2758+ lib tests pass (2 pre-existing backtesting failures unchanged).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:18:57 +01:00
jgrusewski
d6a583e4b3 feat(cuda): wire DSR/n-step config from trainer through to GPU kernel launch
Add use_dsr, dsr_eta, and n_steps fields to ExperienceCollectorConfig
with safe defaults (DSR off, eta=0.01, n_steps=1). Pass them as kernel
args after fill_simulation_enabled, matching the parameter order added
in Tasks 4 and 5. Wire from DqnHyperparams in the trainer hot path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:11:27 +01:00
jgrusewski
2df6a17ee7 feat(cuda): wire branching DQN GPU action selection into trainer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:06:19 +01:00
jgrusewski
61477bc360 feat(cuda): add DSR + n-step to warp experience kernel
Mirror the DSR (Differential Sharpe Ratio) and n-step return
accumulation added by Task 4 to the scalar kernel into the warp
kernel (dqn_full_experience_kernel_warp). DSR/n-step state runs
on lane 0 only, matching the existing reward computation pattern.
All accumulators are reset on episode boundaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:05:54 +01:00
jgrusewski
aee3871788 feat(cuda): add DSR + n-step to scalar experience kernel
Adds Differential Sharpe Ratio reward shaping and n-step return
accumulation to dqn_full_experience_kernel (scalar path). DSR replaces
EMA normalization when use_dsr=1; n-step ring buffer accumulates
discounted returns for effective_n>1. Both are reset on episode
boundaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:03:01 +01:00
jgrusewski
923fab9919 test(cuda): add DSR + n-step CPU parity tests for GPU device functions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:56:03 +01:00
jgrusewski
b11669b17c feat(cuda): add GpuStatistics wrapper with host-side mean/variance computation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:54:25 +01:00
jgrusewski
626255d507 feat(cuda): add select_actions_branching() with allocate-then-DtoD pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:53:47 +01:00
jgrusewski
a168fa86ad feat(cuda): add nstep_push_and_sum() device function for GPU n-step returns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:52:37 +01:00
jgrusewski
4c9dc52fbf feat(cuda): add branching_action_select kernel + composition tests
Append third CUDA kernel to epsilon_greedy_kernel.cu for Branching DQN:
3 independent epsilon-greedy heads (exposure/5, order/3, urgency/3)
compose to factored action index 0-44 (exposure*9 + order*3 + urgency).

Add Rust composition tests verifying full 45-action coverage and
round-trip decomposition correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:45:09 +01:00
jgrusewski
ef7b336818 feat(cuda): add batch_statistics single-block parallel reduction kernel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:44:55 +01:00
jgrusewski
61a9f45af5 feat(cuda): add dsr_step() device function for GPU DSR reward shaping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 12:44:01 +01:00
jgrusewski
ccf9e22a55 fix(clippy): resolve misc warnings across ML crates
- Implement Display trait instead of inherent to_string() (versioning.rs)
- Replace iter().nth() with .get(), .get(0) with .first()
- Collapse else-if chains, derive Default for enums
- Fix deref warnings, redundant let bindings, push_str('\n')
- Convert match-to-if-let, use saturating_sub, RangeInclusive::contains
- Replace vec![push;push] with vec![...] literal (feature_extraction.rs)
- Remove redundant redefinitions, unnecessary parentheses, casts
- Fix &Vec<_> to &[_] in function parameters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:12:14 +01:00
jgrusewski
fca2495a73 fix(clippy): add doc backticks and const fn across ML crates
- Add backticks to type names in doc comments (doc_markdown)
- Mark eligible functions as const fn (missing_const_for_fn)
No behavior changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:51:31 +01:00
jgrusewski
278fd3673f fix(clippy): allow module_name_repetitions and integer_division in ML crates
module_name_repetitions: PpoConfig in ppo module is conventional ML naming.
Renaming breaks every import across the workspace.

integer_division: Basis point calculations, batch size math, combinatorial
formulas — truncation is intentional. Float conversion would introduce bugs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:27:01 +01:00
jgrusewski
7ef92983f9 fix(clippy): apply cargo clippy --fix across workspace
Mechanical auto-fixes: redundant borrows, clone on Copy, or_insert_with,
single-char push_str, get(0) → first(), needless borrow, let_and_return.
150 files, no behavior changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:17:51 +01:00
jgrusewski
c2b6a290e2 ci: trigger warm-cache benchmark on Ubuntu 24.04 images
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:46:35 +01:00
jgrusewski
e6e31305a2 fix(ml-dqn): suppress unused grad_norm_tensor warning without cuda
Prefix with underscore since it's only consumed in #[cfg(feature = "cuda")] block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:33:18 +01:00
jgrusewski
2662d5cda2 fix(build): gate GPU PER calls behind #[cfg(feature = "cuda")]
is_gpu_prioritized() and grad_norm squeeze are only available with CUDA.
Without the gate, compile-services (CPU-only) fails with E0599.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:25:52 +01:00
jgrusewski
9a96691001 perf(dqn): eliminate all GPU→CPU roundtrips from training hot path
Replace per-step to_scalar()/to_vec1() readbacks with GPU-resident
tensor accumulation and single epoch-boundary sync. On H100 this
removes ~12-15 pipeline flushes per training step (~5μs each),
enabling full GPU saturation with zero CPU sync in the inner loop.

Key changes:
- GpuTrainResult: train_step() returns GPU scalar tensors (loss_gpu,
  grad_norm_gpu) instead of f32 — zero readback per step
- Regime conditional: train all 3 heads unconditionally with
  zero-masked weights (mathematical no-op) instead of 3-6
  to_scalar() mask count checks per step
- GPU PER: max_priority as GPU tensor with flush_max_priority(),
  delta-based priority update via index_add (no CPU dedup)
- Deferred diagnostics: NaN detection, CQL logging, Q-value
  estimation all moved to epoch boundary where pipeline is
  already synced
- Legacy CPU-readback wrappers removed entirely

9 files changed, +393/-295 lines. 520 DQN tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:05:18 +01:00
jgrusewski
18166a9e6f perf(gpu): zero-roundtrip DQN experience collection via cuMemcpyDtoDAsync
Eliminate GPU→CPU→GPU roundtrip in the experience collection hot path.
Kernel outputs (states, rewards, actions, dones) now stay on GPU via
device-to-device copy into candle Tensors. Only rewards + actions are
downloaded for monitoring metrics (~0.5MB vs ~3.14GB at 32K episodes).

- Extract launch_kernel() helper from collect_experiences() (DRY)
- Add collect_experiences_gpu() — DtoD path for GPU PER
- Add cuda_slice_to_tensor_f32/i32_to_u32 DtoD copy utilities
- GPU next_states via tensor narrow/cat/reshape (no CPU loop)
- Trainer selects GPU vs CPU path based on is_gpu_prioritized()
- stream.synchronize() barrier ensures cross-stream data visibility

0 warnings, 1266 tests pass (874 ml + 392 ml-dqn)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:05:18 +01:00
jgrusewski
21462d5f0b perf(dqn): eliminate GPU→CPU roundtrips from training hot path
Forward-port from worktree-gpu-hotpath-audit (2 commits):

1. Zero-roundtrip DQN experience collection via cuMemcpyDtoDAsync:
   - GpuExperienceCollector uses device-to-device copies for state tensors
   - Shared memory weight caching in GPU replay buffer
   - NaN priority clamping on GPU (no CPU readback)

2. Eliminate all GPU→CPU roundtrips from training loop:
   - GpuTrainResult: loss + grad_norm stay as GPU scalar tensors
   - Single to_scalar() readback at epoch boundary (not per step)
   - GPU-resident loss accumulation across training steps
   - RegimeConditional head selection via GPU tensor ops
   - Removed per-step NaN diagnostic checks (now epoch-level)

Also removes dead `states_tensor` field from ComputeLossResult and
fixes redundant field name clippy warning.

Expected: ~15-20% training throughput improvement on H100 by
eliminating synchronous GPU→CPU transfers in the inner loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:04:36 +01:00
jgrusewski
645e251fbb fix(docker): add passwd package for useradd on ubuntu:24.04
Ubuntu 24.04 minimal doesn't include passwd (useradd/groupadd).
Debian bookworm-slim had it by default. Required for foxhunt user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:53:27 +01:00
jgrusewski
76bf7d9df1 infra(ci): set CARGO_BUILD_JOBS=14 to match cgroup CPU request
Cargo defaults to /proc/cpuinfo CPU count (32 host cores) but each
compile pod is cgroup-limited to 14 CPU request. Without this, both
pods spawn 32 threads each (64 total) causing heavy throttling on
the 32-core POP2 node. Setting CARGO_BUILD_JOBS=14 per pod avoids
context-switch overhead and uses cgroup-aware parallelism.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:43:50 +01:00
jgrusewski
e4a911941b chore: remove benchmark comment, trigger Docker rebuild + warm cache
Removes temporary benchmark comment. Docker changes from previous commit
will trigger image rebuilds. This run serves as warm-cache benchmark
after cold-cache run completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:35:36 +01:00
jgrusewski
63d8619379 infra(docker): upgrade all images to Ubuntu 24.04 + CUDA 12.9
Align all 4 Docker images with local dev environment:
- ci-builder: CUDA 12.4.1/Ubuntu 22.04 → 12.9.1/Ubuntu 24.04
- ci-builder-cpu: Debian bookworm → Ubuntu 24.04 (+ explicit rustup)
- foxhunt-runtime: Debian bookworm → Ubuntu 24.04
- foxhunt-training-runtime: CUDA 12.6.3 → 12.9.1, nvrtc 12-6 → 12-9

All images now have glibc 2.39, matching the local build machine.
Binaries compiled locally or in CI will run in any of these containers
without GLIBC_2.3x version mismatch errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:34:47 +01:00
jgrusewski
2933e0f014 chore: trigger full CI compile (cold PVC sccache benchmark)
Temporary comment to trigger detect-changes for both services and
training compile steps. Will be removed after benchmarking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:30:51 +01:00
jgrusewski
bcde1c2a95 infra(ci): replace MinIO sccache with local RWO PVCs
Switch sccache backend from S3 (MinIO) to persistent local storage.
Two 20Gi PVCs (sccache-cpu, sccache-cuda) on scw-bssd-retain eliminate
network I/O for cache reads/writes. Both compile steps always run on
the same node so RWO is sufficient.

Removes 13 S3 env vars per compile step, replaces with SCCACHE_DIR=/sccache.
Updates ci-pipeline-template, compile-and-train-template, kustomization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:25:44 +01:00
jgrusewski
ff7d79b03a fix(gpu): resolve 3 pre-existing issues flagged by zen code review
1. PER duplicate index accumulation (HIGH): update_priorities_gpu()
   used index_add delta trick which accumulates deltas for duplicate
   indices, overshooting target priority. Now deduplicates the small
   indices tensor (batch_size=256, ~1KB) via HashMap before delta
   computation. Fast path (no dupes) reuses original tensors.

2. RegimeConditional silent experience drop (MEDIUM):
   insert_batch_tensors() returned Ok(()) for CPU replay buffers,
   silently discarding all GPU-collected experiences. Now converts
   tensors→Vec<Experience> via extracted helper (lazy, only on first
   CPU head encountered) and inserts into CPU buffer.

3. Unsafe direct indexing (LOW): gpu_experience_collector.rs used
   &states[s..e] in fallback paths, violating deny(indexing_slicing).
   Replaced with .get() safe bounds checking.

392/392 ml-dqn + 874/874 ml tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 00:53:16 +01:00
jgrusewski
5149c71444 feat(gpu): warp-cooperative DQN experience kernel for H100 (sm_90+)
Add warp-cooperative CUDA kernel for DQN experience collection that
exploits H100's improved warp shuffle throughput. On sm_90+, 32 threads
(1 warp) cooperate on each dot product via __shfl_xor_sync butterfly
reduction, cutting per-thread register pressure from ~5.5KB to ~200B
and enabling full occupancy on 132 SMs.

Key additions:
- 6 warp-cooperative device functions in common_device_functions.cuh:
  distributed/broadcast matvec (clean + NoisyNet), warp RMSNorm,
  warp_reduce_sum_all
- 4 TILE_LAYER_WARP_* macros using __syncwarp() for warp-level sync
- 3 warp forward passes: standard dueling, NoisyNet, C51 distributional
  (distributed heavy layers + broadcast atom layers for softmax)
- Full warp kernel: dqn_full_experience_kernel_warp with lane-0
  simulation, strided state scatter, warp-shuffle curiosity gather
- Host-side compute capability detection (sm_major >= 9) with
  automatic fallback to standard 256-thread kernel on older GPUs
- cuCtxSetLimit(STACK_SIZE, 16KB) for standard kernel safety

874/874 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 00:44:59 +01:00
jgrusewski
f9b6cdb923 perf(gpu): H100 optimizations — dynamic NOISY_MAX_DIM, 32768 episodes, scratch aliasing
Three zen-validated optimizations for H100 GPU experience collection:

1. Dynamic NOISY_MAX_DIM: inject via NVRTC as max(state_dim, shared_h1,
   shared_h2). On H100 with SHARED_H1=512, noise now covers all 512 input
   dims instead of truncating at 256. Fixes exploration correctness bug.

2. MAX_EPISODES_LIMIT raised 4096→32768: H100 (132 SMs) can now run up to
   32768 episodes per kernel launch = 128 blocks = ~97% SM utilization
   (was 16 blocks = 12%). All 3 trainer.rs caps updated to match.

3. Scratch array aliasing: scratch_v and scratch_a now alias scratch1
   memory after shared layers complete, saving 1024 bytes per thread.
   Per-thread local memory reduced from ~6.5KB to ~5.5KB.

Zen validation confirms: kernel is safe and correct for H100 deployment.
Remaining architectural improvements (warp-cooperative matvec, batched
GEMM) deferred to future optimization phase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:44:35 +01:00
jgrusewski
6b3bb26c05 fix(gpu-per): clamp priorities after index_add to prevent NaN weights
index_add with duplicate indices double-applies deltas — e.g. if index i
is sampled twice with delta -0.5, priority goes 1.0 + (-0.5) + (-0.5) = 0.0,
causing NaN in importance sampling weights. Post-clamp to [epsilon, 1e6]
ensures priorities stay positive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:34:29 +01:00
jgrusewski
9502c1e22c fix(gpu): dynamic SHMEM_MAX_IN_DIM prevents shared memory overflow on H100
Root cause: SHMEM_MAX_IN_DIM was hardcoded to 256, but on H100 with
hidden_dim_base=2048, SHARED_H1=SHARED_H2=512. The cooperative tile
loader wrote 64×512=32768 floats into shmem allocated for 64×256=16384,
causing CUDA_ERROR_INVALID_VALUE on kernel launch.

Fixes:
- Make SHMEM_MAX_IN_DIM injectable via NVRTC #define (was hardcoded)
- Compute max(state_dim, shared_h1, shared_h2) and inject at compile time
- Use dynamic value for host-side shmem_bytes allocation
- Shrink eps_out[NOISY_MAX_DIM] → eps_out[SHMEM_TILE_ROWS] (saves 768B/thread)
- Force smoke tests to Device::Cpu (CUDA driver sensitivity to binary layout)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:31:35 +01:00
jgrusewski
dd075ce02a fix(gpu): cap auto-scaled episode count at GPU buffer limit (4096)
optimal_n_episodes() returns 8192 on H100 (132 SMs) but the GPU
experience collector pre-allocates buffers for MAX_EPISODES_LIMIT=4096.
Exceeding that caused "Episode count exceeds allocated buffer size"
and silent fallback to CPU. Now all 3 auto-scaling sites clamp at 4096.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:43:34 +01:00