|
| 1 | +#!/usr/bin/env julia |
| 2 | +# validate_wavespeed_gpu.jl |
| 3 | +# |
| 4 | +# Validation + benchmark for the batched CUDA wave-speed path |
| 5 | +# (`gpu/wavespeed_gpu.jl`, module `WavespeedGPU`), which inlines the validated device |
| 6 | +# function `WavespeedDev.realize_and_speed_dev` (the per-cell `realize_and_speed`). |
| 7 | +# |
| 8 | +# * HEADLINE gate: GPU (vmin,vmax) for axis 1,2,3 vs the CPU `realize_and_speed` |
| 9 | +# reference battery (8192 real evolved Ma=10/100 states), max REL error |
| 10 | +# |Δv|/max(1,|vref|) over all nb x 3 x 2. GATE ≤ 1e-6 (expect ~1e-8: the 4x4 |
| 11 | +# Schur path already matches LAPACK to ~1e-8). |
| 12 | +# * Benchmark: GPU batched throughput (Mcell/s) solve-only (resident) AND |
| 13 | +# end-to-end (incl H2D/D2H) vs a single-thread CPU baseline (the same scalar |
| 14 | +# device function looped on CPU), batch ~2e6 (=128^3), all 3 axes. |
| 15 | +# |
| 16 | +# ENV: home is OVER QUOTA — read inputs from /storage/scratch1/6/sbryngelson3/gpudata, |
| 17 | +# write nothing under home. Run with gpuenv2, depot on scratch. |
| 18 | + |
| 19 | +import Pkg |
| 20 | +Pkg.activate(joinpath(@__DIR__, "gpuenv2")) |
| 21 | + |
| 22 | +using CUDA, Printf |
| 23 | +include(joinpath(@__DIR__, "wavespeed_gpu.jl")) |
| 24 | +using .WavespeedGPU |
| 25 | +using .WavespeedGPU.WavespeedDev: realize_and_speed_dev |
| 26 | + |
| 27 | +@assert CUDA.functional() "CUDA not functional" |
| 28 | +println("GPU: ", CUDA.name(CUDA.device())) |
| 29 | + |
| 30 | +const DATA = "/storage/scratch1/6/sbryngelson3/gpudata" |
| 31 | + |
| 32 | +# --------------------------------------------------------------------------- |
| 33 | +# 1. Load real battery (HEADLINE) |
| 34 | +# --------------------------------------------------------------------------- |
| 35 | +nb = parse(Int, strip(read(joinpath(DATA, "ws.meta"), String))) |
| 36 | +M = reshape(reinterpret(Float64, read(joinpath(DATA, "ws_M.f64"))), 35, nb) # (35,nb) col=cell |
| 37 | +Wref = reshape(reinterpret(Float64, read(joinpath(DATA, "ws_ref.f64"))), 6, nb) # rows: ax1 vmin,vmax, ax2..., ax3... |
| 38 | +@printf("loaded %d real states (M ∈ [%.3g, %.3g])\n", nb, extrema(M)...) |
| 39 | + |
| 40 | +Mh = Matrix{Float64}(collect(M)) |
| 41 | + |
| 42 | +maxrel = 0.0; maxabs = 0.0; argax = 0; argk = 0; argwhich = 0; ndiv = 0 |
| 43 | +for ax in 1:3 |
| 44 | + vmn, vmx = WavespeedGPU.wave_speeds_batched(Mh, ax) |
| 45 | + rmn = @view Wref[2*ax-1, :] |
| 46 | + rmx = @view Wref[2*ax, :] |
| 47 | + for k in 1:nb |
| 48 | + for (g, r, wch) in ((vmn[k], rmn[k], 0), (vmx[k], rmx[k], 1)) |
| 49 | + a = abs(g - r); e = a / max(1.0, abs(r)) |
| 50 | + if e > maxrel |
| 51 | + global maxrel = e; global argax = ax; global argk = k; global argwhich = wch |
| 52 | + end |
| 53 | + global maxabs = max(maxabs, a) |
| 54 | + if e > 1e-6; global ndiv += 1; end |
| 55 | + end |
| 56 | + end |
| 57 | +end |
| 58 | +@printf("\n=== HEADLINE (GPU wave speeds vs CPU realize_and_speed reference) ===\n") |
| 59 | +@printf("nb=%d axes=3 (total comparisons=%d)\n", nb, nb*6) |
| 60 | +@printf("max REL error |Δv|/max(1,|vref|) = %.3e (gate ≤ 1e-6) [axis %d, cell %d, %s]\n", |
| 61 | + maxrel, argax, argk, argwhich == 0 ? "vmin" : "vmax") |
| 62 | +@printf("max ABS error = %.3e (# comparisons > 1e-6: %d / %d)\n", maxabs, ndiv, nb*6) |
| 63 | +@printf("GATE: %s\n", maxrel <= 1e-6 ? "PASS" : "FAIL") |
| 64 | + |
| 65 | +# --------------------------------------------------------------------------- |
| 66 | +# 2. Benchmark — batch ~2e6 (128^3), all 3 axes |
| 67 | +# --------------------------------------------------------------------------- |
| 68 | +Bbench = 2_097_152 |
| 69 | +@printf("\n=== BENCHMARK (B=%d, summed over 3 axes) ===\n", Bbench) |
| 70 | +Mb = Matrix{Float64}(undef, 35, Bbench) |
| 71 | +@inbounds for k in 1:Bbench |
| 72 | + src = ((k - 1) % nb) + 1 |
| 73 | + for m in 1:35; Mb[m, k] = M[m, src]; end |
| 74 | +end |
| 75 | + |
| 76 | +# --- CPU 1-thread baseline: same scalar device fn looped on CPU (one axis) --- |
| 77 | +function cpu_baseline(Mb, rng, axis) |
| 78 | + s = 0.0 |
| 79 | + @inbounds for k in rng |
| 80 | + a, b = realize_and_speed_dev( |
| 81 | + Mb[1,k], Mb[2,k], Mb[3,k], Mb[4,k], Mb[5,k], Mb[6,k], Mb[7,k], |
| 82 | + Mb[8,k], Mb[9,k], Mb[10,k], Mb[11,k], Mb[12,k], Mb[13,k], Mb[14,k], |
| 83 | + Mb[15,k], Mb[16,k], Mb[17,k], Mb[18,k], Mb[19,k], Mb[20,k], Mb[21,k], |
| 84 | + Mb[22,k], Mb[23,k], Mb[24,k], Mb[25,k], Mb[26,k], Mb[27,k], Mb[28,k], |
| 85 | + Mb[29,k], Mb[30,k], Mb[31,k], Mb[32,k], Mb[33,k], Mb[34,k], Mb[35,k], |
| 86 | + axis, 0.0) |
| 87 | + s += a + b |
| 88 | + end |
| 89 | + s |
| 90 | +end |
| 91 | +cpu_baseline(Mb, 1:1000, 1) # warmup / compile |
| 92 | +ncpu = 50_000 # subset for CPU timing; scale to Mcell/s |
| 93 | +t_cpu = 0.0 |
| 94 | +for ax in 1:3 |
| 95 | + global t_cpu += @elapsed cpu_baseline(Mb, 1:ncpu, ax) |
| 96 | +end |
| 97 | +cpu_rate = (3 * ncpu) / t_cpu / 1e6 |
| 98 | +@printf("CPU 1-thread realize_and_speed_dev: %d cell-axes in %.3f s -> %.4f Mcell/s\n", |
| 99 | + 3*ncpu, t_cpu, cpu_rate) |
| 100 | + |
| 101 | +# --- GPU solve-only (resident) and end-to-end (incl H2D + D2H), summed 3 axes --- |
| 102 | +Md = CuArray(Mb) |
| 103 | +vmn = CUDA.zeros(Float64, Bbench) |
| 104 | +vmx = CUDA.zeros(Float64, Bbench) |
| 105 | +for ax in 1:3 |
| 106 | + WavespeedGPU.wave_speeds_batched!(vmn, vmx, Md, ax) # warmup each axis |
| 107 | +end |
| 108 | +CUDA.synchronize() |
| 109 | + |
| 110 | +t_solve = CUDA.@elapsed begin |
| 111 | + for ax in 1:3 |
| 112 | + WavespeedGPU.wave_speeds_batched!(vmn, vmx, Md, ax) |
| 113 | + end |
| 114 | +end |
| 115 | +solve_rate = (3 * Bbench) / t_solve / 1e6 |
| 116 | + |
| 117 | +t_e2e = CUDA.@elapsed begin |
| 118 | + Md2 = CuArray(Mb) |
| 119 | + vmn2 = CUDA.zeros(Float64, Bbench) |
| 120 | + vmx2 = CUDA.zeros(Float64, Bbench) |
| 121 | + for ax in 1:3 |
| 122 | + WavespeedGPU.wave_speeds_batched!(vmn2, vmx2, Md2, ax) |
| 123 | + end |
| 124 | + a = Array(vmn2); b = Array(vmx2) |
| 125 | +end |
| 126 | +e2e_rate = (3 * Bbench) / t_e2e / 1e6 |
| 127 | + |
| 128 | +@printf("GPU solve-only (resident): %d cell-axes in %.4f s -> %.2f Mcell/s (%.1f× vs CPU)\n", |
| 129 | + 3*Bbench, t_solve, solve_rate, solve_rate / cpu_rate) |
| 130 | +@printf("GPU end-to-end (incl H2D/D2H): %d cell-axes in %.4f s -> %.2f Mcell/s (%.1f× vs CPU)\n", |
| 131 | + 3*Bbench, t_e2e, e2e_rate, e2e_rate / cpu_rate) |
| 132 | + |
| 133 | +@printf("\nDONE\n") |
0 commit comments