Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
df82c9e
GPEC - NEW FEATURE - Record per-stage runtimes in gpec.h5 under Info/…
logan-nc Aug 14, 2026
e695e89
BENCHMARKS - IMPROVEMENT - Report per-stage runtime deltas from Info/…
logan-nc Aug 14, 2026
eb944f9
Merge remote-tracking branch 'origin/refactor/hdf5-metadata' into fea…
logan-nc Aug 14, 2026
a496b73
DOCS - HANDOFF - Add machine-switch handoff document (delete before m…
logan-nc Aug 14, 2026
e232b87
DOCS - HANDOFF - Record post-merge verification and the closed regres…
logan-nc Aug 14, 2026
31285e1
DOCS - HANDOFF - Point the harness caveat at the documented Manifest-…
logan-nc Aug 14, 2026
3d798e2
Merge remote-tracking branch 'origin/refactor/hdf5-metadata' into fea…
logan-nc Aug 15, 2026
c170b96
GPEC - IMPROVEMENT - Gate runtime records on this run writing the out…
logan-nc Aug 15, 2026
fe31278
DOCS - CLEANUP - Remove the machine-switch handoff document
logan-nc Aug 15, 2026
b40006a
Merge remote-tracking branch 'origin/refactor/hdf5-metadata' into fea…
logan-nc Aug 15, 2026
9bd0ef2
TEST - BUGFIX - Exempt Info/Runtimes from the replay bit-for-bit comp…
logan-nc Aug 15, 2026
641504f
Merge remote-tracking branch 'origin/develop' into feature/h5-runtime…
logan-nc Aug 15, 2026
40e550e
GPEC - IMPROVEMENT - Name the tearing runtime record and stamp the fi…
logan-nc Aug 15, 2026
30529ff
Merge origin/develop into feature/h5-runtime-records
priyanshlunia Aug 21, 2026
a643624
Merge origin/develop into feature/h5-runtime-records
logan-nc Sep 28, 2026
24084fc
Repo - FEATURE - Record the ErrorFields stage runtime and mirror its …
logan-nc Sep 28, 2026
b87a66a
Merge remote-tracking branch 'origin/develop' into feature/h5-runtime…
logan-nc Sep 28, 2026
8992b7f
Merge branch 'develop' into feature/h5-runtime-records
logan-nc Oct 1, 2026
3a02f46
Test - TEST - Trim the ErrorFields runtime check to its one decisive …
logan-nc Oct 1, 2026
592cfb5
Repo - FEATURE - Record the ForcingTerms forcing-mode materialization…
logan-nc Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions benchmarks/benchmark_git_branches.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ For each branch/commit, reports:
- Eigenmode energy (et[1]) from gpec.h5
- Integration steps from gpec.h5
- Runtime (averaged over warm runs)
- Per-stage runtime breakdown from `Info/Runtimes` (averaged over warm runs, when both refs record it)
- Git commit hash

# Example Output
Expand All @@ -67,6 +68,12 @@ Comparison:
Eigenmode Δ: +0.0067 (+0.4%)
Steps Δ: -775 (-85.1%)
Runtime Δ: -2.5 s (-1.7% faster)

Per-stage runtime (avg over warm runs):
Stage Branch 1 Branch 2 Δ s Δ %
equilibrium 2.10s 2.08s -0.02 -1.0%
force_free_states 100.40s 98.10s -2.30 -2.3%
total 145.30s 142.80s -2.50 -1.7%
```
"""

Expand Down Expand Up @@ -156,6 +163,28 @@ function checkout_ref(branch, commit)
end
end

# Read the per-stage wall-clock seconds a run recorded under Info/Runtimes.
# Returns an empty Dict for refs that predate the feature, so those still benchmark.
function read_stage_runtimes(gpec_path)
stages = Dict{String,Float64}()
isfile(gpec_path) || return stages
h5open(gpec_path, "r") do h5
haskey(h5, "Info/Runtimes") || return
group = h5["Info/Runtimes"]
for stage in keys(group)
stages[stage] = read(group[stage])
end
end
return stages
end

# Average per-stage runtimes over the warm runs, keeping only stages present in every run.
function average_stage_runtimes(stage_times)
isempty(stage_times) && return Dict{String,Float64}()
shared = intersect(map(keys, stage_times)...)
return Dict(stage => sum(s[stage] for s in stage_times) / length(stage_times) for stage in shared)
end

# Run the example benchmark.
# Each run is a fresh Julia subprocess so that git branch switches take effect
# (using statement only fires once per process; re-checkout without subprocess restart
Expand All @@ -176,19 +205,22 @@ function run_example_benchmark(example_path, num_runs)
println("\n[1/$(num_runs+1)] First run (JIT compilation)...")
run(`julia --project=$project_root $tmpscript $abs_example_path`)

# Warm runs for timing
# Warm runs for timing. Each run overwrites gpec.h5, so the per-stage
# Info/Runtimes record must be collected inside the loop, not after it.
gpec_path = joinpath(abs_example_path, "gpec.h5")
runtimes = Float64[]
stage_times = Vector{Dict{String,Float64}}()
for i in 1:num_runs
println("\n[$((i+1))/$(num_runs+1)] Warm run $i...")
t_start = time()
run(`julia --project=$project_root $tmpscript $abs_example_path`)
runtime = time() - t_start
push!(runtimes, runtime)
push!(stage_times, read_stage_runtimes(gpec_path))
println(" Runtime: $(round(runtime, digits=2)) s")
end

# Extract metrics from gpec.h5
gpec_path = joinpath(abs_example_path, "gpec.h5")
if !isfile(gpec_path)
error("gpec.h5 not found at $gpec_path")
end
Expand All @@ -203,13 +235,33 @@ function run_example_benchmark(example_path, num_runs)
return (
eigenvalue=real(et[1]),
steps=nsteps,
runtime=avg_runtime
runtime=avg_runtime,
stages=average_stage_runtimes(stage_times)
)
finally
rm(tmpscript; force=true)
end
end

# Per-stage runtime breakdown (Info/Runtimes), averaged over the warm runs of each branch.
# Silently skipped when either side lacks the record, so comparisons against older refs work.
function print_stage_comparison(r1, r2)
shared = intersect(keys(r1.metrics.stages), keys(r2.metrics.stages))
isempty(shared) && return nothing
ordered = sort(collect(shared); by=stage -> (stage == "total", stage))

println("\nPer-stage runtime (avg over warm runs):")
@printf(" %-22s %10s %10s %10s %9s\n", "Stage", "Branch 1", "Branch 2", "Δ s", "Δ %")
for stage in ordered
t1 = r1.metrics.stages[stage]
t2 = r2.metrics.stages[stage]
# A stage that ran in no measurable time has no meaningful percentage baseline.
pct = t1 == 0 ? "—" : @sprintf("%+.1f%%", 100 * (t2 - t1) / t1)
@printf(" %-22s %9.2fs %9.2fs %+9.2f %9s\n", stage, t1, t2, t2 - t1, pct)
end
return nothing
end

# Main benchmarking function
function benchmark_branches(options)
println("="^60)
Expand Down Expand Up @@ -293,6 +345,8 @@ function benchmark_branches(options)
speedup_desc = time_delta < 0 ? "faster" : "slower"
@printf(" Runtime Δ: %+.2f s (%+.1f%% %s)\n", time_delta, abs(time_pct), speedup_desc)

print_stage_comparison(r1, r2)

println("\n" * "="^60)

# Write to output file if requested
Expand Down
2 changes: 1 addition & 1 deletion docs/development/hdf5-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Top level (11 groups):

| Group | Contents |
|---|---|
| `Info/` | Run metadata: `git_version`, mode-number ranges (`mpert`, `mlow`, …, `mn_index`), `psilim`, `qlim` |
| `Info/` | Run metadata: `git_version`, mode-number ranges (`mpert`, `mlow`, …, `mn_index`), `psilim`, `qlim`, `Runtimes/` (per-stage wall-clock seconds) |
| `Input/` | Rerun snapshot: `gpec_toml_raw`, `RawInputs/{Equilibrium, ForcingTerms, Coils/<name>}` |
| `Equilibrium/` | Scalars (`beta_N`, `q_axis`, `q_95`, `I_p`, …) plus `Profiles/` (1-D on `psi`: 2piF, mu0p, dVdpsi, q) and `Geometry/` (2-D on `psi`×`theta`: rcoords, offset, nu, jac) |
| `ForceFreeStates/` | `Solutions/ForwardIntegration/` (u-solutions), `Solutions/GalerkinIntegration/` (closed ξ profiles in the shared layout, `Match/` diagnostics, the gal surface list, debug-gated `Basis/`), `EulerLagrangeMatrices/{Ideal,Kinetic}`, `FreeBoundaryStability/`, `EdgeScan/` |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ All results are written to a single HDF5 file (default: `gpec.h5`). The top-leve

| Group | Contents |
|---|---|
| `Info/` | Run metadata: git version, mode-number ranges, ψ limit |
| `Info/` | Run metadata: git version, mode-number ranges, ψ limit, `Runtimes/` (per-stage wall-clock seconds) |
| `Input/` | Self-contained rerun snapshot: merged TOML blob, raw equilibrium/forcing/coil inputs |
| `Equilibrium/` | Equilibrium scalars (`beta_N`, `q_axis`, `q_95`, …), 1-D profiles (`Profiles/`), 2-D geometry (`Geometry/`) |
| `ForceFreeStates/` | Stability solve: `Solutions/{ForwardIntegration,GalerkinIntegration}`, `EulerLagrangeMatrices/`, `FreeBoundaryStability/`, `EdgeScan/` |
Expand Down
Loading
Loading