Skip to content

P4 native CUDA tuning

P4 keeps the strict numerical engine as the default and adds two explicit, independent tuning switches for backend="native_cuda":

model = RenewableHuberRegressor(
    backend="native_cuda",
    device="cuda",
    dtype="float32",
    cuda_graphs=True,
    cuda_fast_math=True,
)

cuda_graphs=True captures the stable candidate-objective DAG for one native update and replays it across Newton iterations and line-search trials. Since P7 that DAG is the fused line-search round (see P7 optimizations): a parameter copy from pinned memory, two kernels and a result copy, so one graph serves every round of the update and stays bit-identical to the stream path. Profiling such a capture needs Nsight's --cuda-graph-trace=node. A graph is scoped to that update, so captured pointers never outlive a borrowed DLPack tensor and a new batch shape/configuration cannot replay stale arguments. Capture is best effort. If the runtime or a library call rejects capture, the engine clears its enabled graph flag, increments graph_fallbacks, and runs the original stream path. State remains transactional: coefficients and the information matrix are swapped into the active state only after successful stream completion.

cuda_fast_math=True is valid only with dtype="float32". It opts the main cuBLAS handle into TF32 Tensor Core math. Objective reductions, convergence norms, and the float64 path retain strict math. The accepted comparison against strict float32 is rtol=5e-3, atol=5e-4; callers requiring reproducible strict float32 results must leave the option disabled.

The C ABI remains version 1. Tuning flags use the previously reserved engine option word, and rh_cuda_engine_features is an additive symbol. The Python payload API is version 3 and reports supports_cuda_graphs and supports_fast_math. After fitting, model.cuda_features_ exposes requested and enabled flags plus capture/replay/fallback counters. Both constructor values participate in get_params, scikit-learn cloning, and checkpoints.

RTX 5070 Ti historical development observation

The following local tuning run used a 32,768 x 256 float32 batch, three warmups, nine samples, and at least 0.5 seconds of fixed work per statistical sample. Its raw JSON was not committed, so these figures are historical engineering context, not retained 0.6.1 release evidence or a current performance claim:

python scripts/benchmarks/benchmark_cuda_tuning.py `
  --samples 32768 --features 256 --dtype float32 `
  --warmup 3 --repeats 9 --minimum-sample-seconds 0.5 `
  --output artifacts/p4-windows-rtx5070ti-cuda-tuning.json

On that development run, strict execution measured 19.416 ms, CUDA Graph execution 17.021 ms (1.14x), and Graph+TF32 18.162 ms (1.07x versus strict). The 1.07x TF32 difference is inside this host's approximately 10% noise band and is not evidence of a speedup. Relative MAD was 0.79%, 3.85%, and 2.49%, respectively. Graph capture recorded 4,522 replays with zero fallback. TF32 did not beat strict-precision Graph for this shape, so it remains an experimental opt-in rather than an automatic dispatch choice. Coefficient error versus strict was also recorded in the JSON and stayed within the declared tolerance.

Nsight reproduction

.\scripts\profiling\run_nsight_systems.ps1 `
  -Python python `
  -Engine native_cuda -DType float32 -CudaGraphs

.\scripts\profiling\run_nsight_compute.ps1 `
  -Python python `
  -Engine native_cuda -DType float32 -CudaGraphs -CudaFastMath

python scripts/profiling/profile_cuda_update.py `
  --engine native_cuda --input-location host --dtype float32 `
  --cuda-graphs --cuda-fast-math `
  --metadata-output artifacts/p4-nsight-metadata.json

Nsight evidence is valid only when the JSON metadata, .nsys-rep, GPU, driver, Toolkit, shape, transport, and tuning flags are retained together. CUDA API/kernel/memcpy totals overlap and must not be added as wall time.

The uncommitted 2026-08-01 Nsight Systems capture for the same workload was stored locally under artifacts/nsight-p4. All three profiled updates converged in seven iterations; their median was 18.358 ms and the NVTX repeat window was 56.159 ms. Nsight observed 3 graph instantiations and 84 graph launches. The report also makes the remaining bottleneck explicit: 98 cudaStreamSynchronize calls consumed 27.451 ms of CUDA API time, so device-side convergence is still the next architectural optimization. These API totals overlap GPU work and are evidence for prioritization, not an additive wall-time model.