Commit Graph

5645 Commits

Author SHA1 Message Date
jgrusewski
a9c417a79e perf(rl): replace ALL cudarc memcpy/sync with raw CUDA API in hot path
15 diag staging DtoD + 1 FRD labels DtoD + diag sync all converted
to raw_memcpy_dtod_async / raw_stream_sync. Zero cudarc driver calls
in steady-state step loop.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:20:30 +02:00
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
2bcfadf5f4 fix(infra): auto-detect CUDA_COMPUTE_CAP from nvidia-smi + show build stderr
Sets CUDA_COMPUTE_CAP dynamically from the GPU on the node. Clears
both ml-alpha and ml-core build caches. Captures build.rs stderr in
pod logs for debugging.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:03:12 +02:00
jgrusewski
c899fe9f1b fix(infra): remove hardcoded CUDA_COMPUTE_CAP=89 — let nvidia-smi auto-detect
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:57:20 +02:00
jgrusewski
b05dc1b40d fix(build): auto-detect GPU arch — sm_90 on H100, sm_89 on L40S, sm_86 local
Replace fatbin with single-arch cubin auto-detected via nvidia-smi.
Each node compiles for its own GPU — faster builds, guaranteed compat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:51:10 +02:00
jgrusewski
1668eb4283 fix(infra): clear stale ml-alpha build cache for fatbin migration
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:44:08 +02:00
jgrusewski
b531bd7148 perf(rl): mapped-pinned ISV + loss — zero DtoD copies forever
Replace isv_d CudaSlice with isv_mapped MappedF32Buffer. GPU writes
via dev_ptr, host reads via host_ptr. Zero copies, zero syncs.
Same for 4 loss accumulators (q/pi/v/frd).

Delete: isv_host Vec, isv_staging, loss_staging, loss_dtod_done_event,
all DtoD copies for ISV/losses, all event record/sync for losses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:27:39 +02:00
jgrusewski
0064ad17fa perf(rl): mapped-pinned ISV + loss — zero DtoD copies forever
Replace isv_d CudaSlice with isv_mapped MappedF32Buffer. GPU writes
via dev_ptr, host reads via host_ptr. Zero copies, zero syncs.
Same for 4 loss accumulators (q/pi/v/frd) and 2 entropy accumulators.

Delete: isv_host Vec, isv_staging, frd_loss_staging, scalar_staging,
loss_staging_3, loss_dtod_done_event, all DtoD copies for ISV/losses,
all event record/sync for losses.

ISV is ~2.3KB, losses are 24B — PCIe bandwidth penalty negligible.
Mapped-pinned dev_ptr is stable → CUDA graph capture unaffected.

External head methods (DqnHead, PolicyHead, ValueHead, FrdHead)
changed from &CudaSlice<f32> to &u64 for ISV/loss device pointers —
.arg(&u64) pushes the same 8-byte CUdeviceptr as .arg(&CudaSlice).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:25:08 +02:00
jgrusewski
b5e99472c9 perf(rl): bypass cudarc entirely — raw graph launch + sync + events
Replace all cudarc graph.launch(), event.record/synchronize(),
stream.wait(), and memset_zeros in the hot path with direct CUDA
driver API calls. Eliminates cudarc's internal bind_to_thread +
event tracking overhead (~200us per call x ~40 calls/step).

Steady-state hot path is now zero cudarc calls:
- 4x graph.launch() → raw_graph_launch (cuGraphLaunch)
- 1x event.synchronize() → raw_event_sync (cuEventSynchronize)
- 5x event.record() → raw_event_record (cuEventRecord)
- 4x stream.wait() → raw_stream_wait_event (cuStreamWaitEvent)
- 28x memset_zeros → raw_memset_d8_zero (cuMemsetD8Async)

Also removed 36 redundant memset_zeros inside training graph
capture — backward kernels overwrite every element, so these
burned memset bandwidth on every graph replay for zero benefit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:47:02 +02:00
jgrusewski
707e0dfe85 perf(rl): eliminate last hot-path sync — fully async loss readback
Async loss readback via loss_dtod_done_event — zero syncs in steady-
state step. Return previous step's cached stats (one-step delay).
DtoDs complete by stream ordering before next step's event wait.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:23:37 +02:00
jgrusewski
1d167af7b0 perf(diag): migrate snapshot_async to raw ptrs — 15 fewer device_ptr() per step
Replace 15 CudaSlice device_ptr() calls (each triggers cudarc event
tracking) with direct raw_ptr() u64 reads. Zero CUDA driver overhead
in the diag staging path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:20:02 +02:00
jgrusewski
148454d37d feat(cuda): curriculum weights E8 + adversarial regime boost kernels
rl_curriculum_weights: per-segment Sharpe → z-score → softmax difficulty
weights. Harder segments sampled more. Block tree-reduce, no atomics.

rl_adversarial_boost: multiply PER priority by boost factor for
negative-reward transitions. Self-regulating — fewer losses → fewer
boosts. ISV-driven threshold + boost magnitude.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:00:21 +02:00
jgrusewski
4223ba676f fix(build): fat cubins sm_86+sm_89+sm_90 — RTX 3050 + L40S + H100
Replace -cubin -arch=sm_XX with -fatbin -gencode for all 3 GPU archs.
All include_bytes! paths updated from .cubin to .fatbin. Removes
CUDA_COMPUTE_CAP env var dependency. cuModuleLoadData transparently
selects the right arch from the fat binary at runtime.

Fixes CUDA_ERROR_NO_BINARY_FOR_GPU on H100 (sm_90).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 11:58:14 +02:00
jgrusewski
63e447a182 feat(rl): outcome head backward + diag JSONL enrichment
Wire outcome CE backward → grad_W/b → Adam → grad_h_accumulate.
Add PopArt, spectral, Q-bias, per-branch LR, outcome metrics to
diag JSONL for training validation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 11:47:19 +02:00
jgrusewski
823a15f282 feat(scripts): alpha-rl-run.sh production wrapper + session memory updates
One-command production run: push, submit, monitor, download, analyze.
Memory: cudarc overhead pearl, nsys workflow reference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 11:39:53 +02:00
jgrusewski
e5a69a589b perf(rl): amortize spectral norm + eliminate hot-path sync/device_ptr/alloc
- Spectral norm: every 100 steps (843us/step -> 8.4us amortized)
  via Weyl's inequality (sigma_max shifts <= ||DW||_2 per Adam step)
- Replace train_stream.synchronize() with event-based GPU-side wait
  (train_done_event record/wait pattern, no host stall)
- Convert dqn_replay_step Q-loss readback to deferred one-step-delayed
  pattern: async DtoD into scalar_staging, read previous value via
  volatile read (eliminates sync + device_ptr per replay iteration)
- Delete dead read_scalar_via_staging (last caller removed)

Steady-state hot path (steps 3+) now has:
  - 1 synchronize (step_synthetic batched loss, unavoidable)
  - 0 device_ptr() calls
  - 0 per-step allocs
  - 0 read_scalar_via_staging
  - Spectral norm: 3 launches / 100 steps (was 3 / step)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 11:15:43 +02:00
jgrusewski
4a4e3171a7 perf(rl): pre-extract raw CUdeviceptr — zero device_ptr() in hot path
Cache all hot-path device pointers as raw u64 in HotPathPtrs struct at
trainer init. Replaces 9 device_ptr()/device_ptr_mut() calls (each
triggers cudarc event wait+record → cuStreamSynchronize) with direct
u64 reads from the cached struct.

Affected call sites:
- ISV staging DtoD (step_synthetic)
- Batched loss readback: pi/Q/V/FRD DtoD copies (step_synthetic)
- h_t→h_tp1 encoder DtoD copy (step_with_lobsim)
- trade_context/multires_output ptr pass to perception encoder

All pointers are stable (buffers pre-allocated at init, never
reallocated). Utility functions (read_slice_d, write_slice_f32_d, etc.)
retain device_ptr() as they operate on arbitrary caller-provided buffers
and each already does its own stream sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 10:46:43 +02:00
jgrusewski
41881f7724 perf(rl): migrate 25 hot-path launches to raw cuLaunchKernel
Replace all launch_builder().arg().launch() outside CUDA graph capture
with raw_launch() calling cuLaunchKernel directly. Eliminates ~200μs
per-launch cudarc overhead (trait dispatch, Arc, event guards).

25 launches migrated: PER push/sample/update/rebuild, HER track/inject
/forward, fused controllers, EMA producers, LR controller, spectral
norm, Q-bias, per-branch LR, grad accumulate, reduce_axis0.

61 launches inside graph capture regions kept as launch_builder
(only execute during warmup/capture steps 0-1).

Expected: ~7ms/step host overhead eliminated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 10:15:40 +02:00
jgrusewski
7e12099934 feat(rl): wire P1+P2 — PopArt, spectral norm/decouple, outcome head, Q-bias, per-branch LR
All unconditional. No feature flags. ISV controls magnitudes.

PopArt normalize replaces apply_reward_scale in step_with_lobsim,
with V-correct on v_pred_d and v_pred_tp1_d. Spectral norm runs
one power iteration per step on DQN/IQN/policy weight matrices
after each Adam step in dqn_replay_step. Spectral decouple adds
lambda*mean(logits^2) penalty to Q and pi loss in step_synthetic.
K=3 outcome classifier forward+CE loss runs each step; labels
assigned from reward/done signals. Q-bias correction tracks
mean(Q - return) and emits correction to ISV. Per-branch LR
controller adjusts per-head LR scale factors from loss deltas.

ISV slots 553-572 bootstrapped. Per-branch LR kernel ABI fixed:
current losses passed as args (eliminates slot 572-575 collision
with RL_OUTCOME_AUX_LAMBDA_INDEX).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:49:57 +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
dac9cfef5c spec: bypass cudarc for hot path — raw CUDA driver API + mega-kernel fusion
cudarc adds 18.8ms/step overhead at b=256 (GPU kernels take 0.16ms).
Phase 1: raw cuLaunchKernel wrapper. Phase 2: pre-extracted raw ptrs.
Phase 3: fused mega-kernels (20 launches → 3).
Target: 1ms/step → 500+ sps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:39:17 +02:00
jgrusewski
62adf13187 feat(rl): P1+P2 kernels — PopArt, spectral norm, outcome head, Q-bias, per-branch LR
9 new CUDA kernels + OutcomeHead Rust struct + ISV slots 553-572:

PopArt: Welford-EMA reward normalization + V-head correction
Spectral: power iteration σ_max + L2 logit decoupling penalty
Outcome: K=3 trade-outcome classifier (Profit/Timeout/Loss)
Q-bias: EMA correction clipped ±10 for Bellman targets
Per-branch LR: adaptive per-head [0.5, 2.0] scaling from loss improvement

All unconditional — no feature flags. ISV-driven magnitudes.
No atomicAdd. Pre-compiled cubins.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:36:21 +02:00
jgrusewski
99707f7e4e perf(rl): eliminate 5 of 7 stream.synchronize() from step hot path
Replace per-step cuStreamSynchronize calls with deferred-read and
event-based cross-stream sync patterns:

- ISV staging reads (2 syncs): use previous step's staging data
  (one-step delay acceptable for slow-moving EMA controllers).
  Queue DtoD async for next step's read, flushed by batched
  loss sync.

- Loss scalar reads (3 syncs → 1): batch 3 individual
  read_scalar_via_staging calls (each with DtoD+sync) + FRD
  DtoD+sync into 4 concurrent DtoDs + 1 sync via pre-allocated
  loss_staging_3 MappedF32Buffer.

- Cross-stream syncs (3 syncs → 0 host-blocking): replace
  cuStreamSynchronize with CudaEvent record/wait pairs for
  push_done, sample_done, replay_done inter-stream dependencies.
  Events sync GPU timelines without blocking the host.

Hot path: 7 explicit syncs → 1 batched loss sync + 1 step-start
train_stream sync. Expected: ~5× reduction in host-wait time
per step (14ms → 2-4ms).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:26:16 +02:00
jgrusewski
274ff82494 perf(rl): eliminate per-step mapped-pinned allocs — pre-allocated staging
Replace 3 read_slice_d calls (each allocs+frees MappedF32Buffer) and
4 read_scalar_d calls with pre-allocated persistent isv_staging,
frd_loss_staging, and scalar_staging buffers. Deletes the now-unused
read_scalar_d function.

Eliminates ~20 cuMemHostAlloc + ~20 cuMemFree per step from the hot
path. GPU completes all kernels in 160us/step — host overhead was 14ms.
Target: <1ms host overhead at b=256.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:12:35 +02:00
jgrusewski
e0ea71e90e spec+plan: DQN concepts adoption — PopArt, spectral norm, outcome head, enrichment E1-E8
7 tasks: PopArt normalization, spectral norm+decoupling, K=3 outcome
aux head, Q-bias correction, per-branch LR, curriculum weights,
adversarial regime injection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:59:51 +02:00
jgrusewski
4fc7d63185 feat(rl): wire bidirectional HER — track + inject + forward in step pipeline
Backward HER: on trade close, injects synthetic with peak PnL if
peak > 1.5× actual. Forward HER: evaluates closed trades after 50
steps, injects if holding would have been better.

ISV bootstrap: threshold=1.5, priority_boost=3.0, lookahead=50.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:48:11 +02:00
jgrusewski
029f2956f1 feat(cuda): bidirectional HER kernels — track, inject, forward
rl_hindsight_track: per-step mid accumulation + peak tracking.
rl_hindsight_inject: backward HER on done, prefix-sum replay write.
rl_hindsight_forward: forward HER after lookahead, prefix-sum write.
No atomicAdd.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:41:05 +02:00
jgrusewski
95af0db54f feat(rl): GpuHindsight struct + ISV slots for bidirectional HER
Device-resident buffers: mid_ring (backward peak tracking),
closed_ring + closed_h_t (forward continuation evaluation).
ISV slots 549-552 for threshold, priority_boost, inject_count,
forward_lookahead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:36:05 +02:00
jgrusewski
b06355456a feat(rl): multi-stream — train_stream for PER sample/priority/rebuild
PER sample, update_priority, and tree_rebuild run on a dedicated
train_stream. The env-step pipeline (encoder through push_flush)
runs on the main stream. Sync points:

  1. Top of step_with_lobsim: train_stream.synchronize() ensures
     previous step's priority/rebuild completed before push touches
     the replay buffer.
  2. Before K-loop: stream.synchronize() ensures push_flush is done
     before train_stream's rl_per_sample reads the replay buffer.
  3. After rl_per_sample: train_stream.synchronize() ensures sampled_*
     buffers are written before step_synthetic/dqn_replay_step reads
     them on the main stream.
  4. After dqn_replay_step (k>0 path): stream.synchronize() ensures
     td_per_sample_d is written before train_stream's priority update.
     (step_synthetic already syncs internally.)

The last K-iter's priority update + tree rebuild are NOT synced at
step end — they overlap with the next step's encoder forward on the
main stream, hiding ~1ms of PER maintenance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:55:56 +02:00
jgrusewski
aab7a0b180 feat(rl): wire coalesced push_ring + push_flush, delete old rl_per_push
Two-kernel PER push: ring (Grid=B, Block=128 coalesced h_t copy) +
flush (block-0 prefix-sum + coalesced replay write). Replaces the old
single-block sequential kernel. Expected 8× faster (124μs → ~15μs).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:47:57 +02:00
jgrusewski
9ad7eb257c feat(cuda): coalesced PER push — ring + flush kernels (Grid=B, Block=128)
rl_per_push_ring: 128 threads copy h_t in one coalesced 512-byte
transaction. Thread 0 writes scalars + flush_flag.

rl_per_push_flush: block 0 prefix-sums flush_flags, signals via
volatile ready flag. All flushing blocks do coalesced 128-thread
h_t + h_tp1 + scalars write to replay. No atomicAdd.

Expected: 124μs → ~15μs per step (8× faster).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:41:15 +02:00
jgrusewski
be3d5ceeae plan: multi-stream + per-push rewrite — 5 tasks
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:32:56 +02:00
jgrusewski
63416f7c12 spec: multi-stream pipeline + per-push rewrite — target 100 sps at b=256
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:30:29 +02:00
jgrusewski
09873de1f5 perf(rl): zero-HtoD FRD labels via mapped-pinned staging + make stream pub
Replace write_slice_i32_d_pub (sync + alloc + DtoD) with persistent
MappedI32Buffer staging. Host writes to host_ptr, async DtoD to
frd_labels_d on the train stream — zero sync, zero alloc per step.

Also: make stream field pub for external DtoD access.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:21:07 +02:00
jgrusewski
e716d57d6f perf(cuda): vectorize rl_per_push — float4 loads (4× fewer memory transactions)
Replace 3 sequential 128-float copy loops with 32 float4 vectorized
loads each. Reduces per-thread memory transactions 4× for the heaviest
kernel (46% of GPU time at 123μs/call → expected ~30μs).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:14:17 +02:00
jgrusewski
5f3e3578a0 perf(diag): move ALL remaining device reads to async staging — zero syncs
Expanded DiagStaging with 8 more fields (position_lots, pyramid_count,
unit_entry_price, unit_entry_step, unit_lots, unit_trail,
close_unit_index, frd_logits). Eliminates 36 stream syncs per step
that consumed 90.9% of CUDA API time per nsys profile. unit_active
derived from unit_lots != 0 on host (avoids u8-to-f32 DtoD mismatch).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:11:59 +02:00
jgrusewski
5dd5a08963 feat(infra): optional nsys profiling in Argo template (-p nsys-profile=true)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:00:42 +02:00
jgrusewski
1ddf74fb51 fix(diag): replace hardcoded replay_len=0 with actual GPU PER capacity
The old host replay.len() was replaced with 0usize during T1 cleanup.
GPU PER is working fine — just not reporting its length in the diag.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:58:48 +02:00
jgrusewski
33e14155fe plan: bidirectional HER — 5 tasks (struct, kernels, wiring, diag, tests)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:49:42 +02:00
jgrusewski
ee2cc147f6 feat(diag): async non-blocking staging via separate stream + double-buffer
DiagStaging runs DtoD copies on a separate CUDA stream — zero stalls
on the training pipeline. Double-buffered mapped-pinned: host reads
previous step's data while current step's copies run concurrently.

Replaces 7 blocking read_slice_d calls (ISV, rewards, dones, actions,
raw_rewards, trade_duration, outcome_ema) with async staging reads.
One-step delay on diag data (acceptable for diagnostics).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:48:00 +02:00
jgrusewski
6f973b3a3f spec: bidirectional HER — backward peak + forward continuation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:37:30 +02:00
jgrusewski
6674007952 feat(rl): wire GPU PER — push/sample/update/rebuild all device-side
Replaces todo!() stubs with kernel launches. GpuReplayBuffer field,
4 cubin loads, full per-step pipeline:
1. rl_per_push after reward extraction (n-step + circular write)
2. rl_per_sample in K-loop (tree walk + gather to sampled_*_d)
3. rl_per_update_priority + tree_rebuild after step_synthetic

Zero host-side PER operations. Per feedback_cpu_is_read_only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:36:34 +02:00
jgrusewski
c2825a7928 spec: GPU-native Hindsight Experience Replay for wave-exit timing
Backward-looking HER: on trade close, compute peak unrealized PnL
during the trade's lifetime. If peak >> actual, inject synthetic
transition with peak_pnl and boosted priority. Teaches the agent
optimal wave-exit timing 10× faster than sparse reward alone.

Two kernels: rl_hindsight_track (per-step mid accumulation) and
rl_hindsight_inject (synthetic push on done with coordination).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:35:18 +02:00
jgrusewski
c95abbf3da feat(cuda): GPU-resident PER kernels — push, sample, update, tree_rebuild
4 kernels for all-device prioritized experience replay:
- rl_per_push: n-step accumulation + single-block prefix-sum for
  write_head coordination (no atomicAdd)
- rl_per_sample: stratified proportional sampling via top-down
  sum-tree walk with xorshift32 PRNG + inline gather
- rl_per_update_priority: write |TD|^α to leaves + shared-mem
  block-wide max reduction
- rl_per_tree_rebuild: bottom-up parallel scan with __threadfence
  between levels (15 passes for capacity=32768, no atomics)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:25:31 +02:00
jgrusewski
787418e779 fix(rl): trail distance units mismatch — was 400× too small
trail_distance was computed from mean_abs_pnl (scaled reward space)
but compared against mid-entry (price space). At reward_scale=0.0024,
the trail was 0.002 price units = effectively zero, causing immediate
trail-stop on every trade regardless of min_hold.

Fix: divide by reward_scale to recover price-space magnitude:
  trail = k_init × (mean_abs_pnl / reward_scale)

This gives trail ≈ 0.8 ticks at k_init=2.0 — reasonable for ES.
avg_hold should now respect min_hold (100 steps) since trail stops
won't fire on the first tick.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:24:48 +02:00
jgrusewski
e30f9721e9 feat(rl): GpuReplayBuffer struct — device-resident circular replay + sum-tree
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:20:28 +02:00
jgrusewski
ef23dfb3d9 refactor(rl): delete host-side PER + diag-every band-aid (greenfield prep)
Remove: ReplayBuffer, Transition, NStepEntry, push_to_replay,
sample_and_gather, n_step_buffer, replay.rs, --diag-every flag.
PER call sites stubbed with todo!() — replaced by GPU PER in next commits.

Also fixes pre-commit hook to allow todo!() macro (per CLAUDE.md:
"todo!() macro is OK for runtime stubs") while still blocking
TODO/FIXME comment markers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:18:11 +02:00
jgrusewski
766adbc718 plan: GPU-resident PER + async diag — 7 tasks, greenfield
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:04:03 +02:00
jgrusewski
3f61d18735 spec: GPU-resident PER + async non-blocking diag streaming
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:00:00 +02:00
jgrusewski
3541ba7352 perf(diag): add --diag-every flag + fix PnL accumulation bug
- --diag-every N (default 100): skip device readback on non-diag steps.
  Eliminates ~10 stream.sync per step → ~2× throughput at large batch.
- Fix PnL bug: was accumulating raw_rewards (shaped, all batches) instead
  of rewards/scale on done steps only.
- Wire diag-every=100 in Argo template (diag JSONL every 100 steps).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:56:21 +02:00