Research-0764: psnr_hvs CUDA kernel — F3 __ldg() + __launch_bounds__(64) (2026-05-29)¶
Context: PR #96 candidate #5 — applies the F3 struct-by-value fix to the psnr_hvs kernel in core/src/feature/cuda/integer_psnr_hvs/psnr_hvs_score.cu.
Background — F3 pattern¶
ADR-0754 (PR #93) established the pattern: extract const float *__restrict__ pointers from VmafCudaBuffer struct arguments before inner loops, then use __ldg() for all reads. Passing VmafCudaBuffer by value hides the underlying device pointer from the nvcc compiler's non-coherent-load analysis; the extraction makes the alias-free invariant visible, routing loads through the L1 read-only texture cache (LDG.E.CONSTANT instruction in SASS).
ADR-0757 (PR #96 fix #2) applied the same pattern to ms_ssim_horiz and ms_ssim_vert_lcs, both 128-thread kernels with K=11 inner loops.
psnr_hvs kernel specifics¶
| Property | Value |
|---|---|
| Block config | 8x8 = 64 threads |
| Loads per block | 64 reads from ref_buf + 64 reads from dist_buf = 128 total |
| Loop structure | Cooperative tile load (all 64 threads load 1 element each) |
| Input buffers | 2 (ref, dist) |
__ldg() calls added | 2 (one per pointer, one element per thread) |
Change summary¶
VmafCudaBuffer ref_in, dist_inremain by-value in the kernel signature (ABI unchanged).const float *__restrict__ ref_bufandconst float *__restrict__ dist_bufextracted from.dataonce before theif (valid_block)branch.__ldg(&ref_buf[src_idx])and__ldg(&dist_buf[src_idx])used for the two element loads inside the valid-block guard.__launch_bounds__(64)added — matches the actual 8x8 block launch ininteger_psnr_hvs_cuda.c; mirrors ADR-0754 / ADR-0757 pattern.
Predicted performance¶
Based on ADR-0754 live ncu measurements on RTX 4090 sm_89:
| Resolution | Expected change | Confidence |
|---|---|---|
| 576p | noise-dominated (wave-limited regime, < 0.5 waves) | low |
| 1080p | -3 to -5% kernel duration (DRAM-bound regime) | medium |
| 4K | -5 to -8% kernel duration (deep DRAM-bound, multi-wave) | medium |
The psnr_hvs kernel loads 2 buffers vs vert_combine's 5, so the absolute L2-pressure reduction is proportionally smaller. However the 64-thread block configuration (vs 128 for SSIM) means higher occupancy sensitivity to register spilling — __launch_bounds__(64) provides the budget guard.
Correctness¶
No arithmetic is changed. The cooperative tile load pattern and all subsequent shared-memory reductions, DCT passes, and float accumulations are untouched. Scores must be bit-identical to the pre-patch kernel (ADR-0214 places=4 gate).
Live ncu A/B measurements¶
Pending — to be filled in after container build + ncu run.
Verdict¶
READY — same pattern as ADR-0754 / ADR-0757, no arithmetic change, no ABI change, bit-identical scores expected.