rtx-fsi: wall-split instrumentation — the STRUCTURE is 79% of the coupled phase
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Build (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s

Per-pass timers (reporting-only, default digits verified identical)
around the three pass components. Measured on the FSI3 committed
default: fluid 99 s (19.7%), structure 397 s (79.2%), load sampling
0.9%, state save 0.0%. The flag's Newton step costs ~195 ms/pass
against ~0.5 ms of element assembly and a ~10 ms-scale 570-DOF
solve — either the stepper carries ~20x implementation overhead or
the ROM upside is enormous; the ECSW campaign's re-scope decision
inverts accordingly (profile the stepper next).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
This commit is contained in:
Omar Sobh
2026-08-29 16:50:28 -05:00
co-authored by Claude Fable 5
parent 50e382c046
commit 8a8da2383d
@@ -423,6 +423,14 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
w
});
// Wall-split accumulators (reporting-only): where a coupled pass
// actually spends its time — the measurement that decides whether a
// structural ROM can matter (ECSW campaign, re-scope decision).
let t_fluid = std::cell::Cell::new(0.0f64);
let t_structure = std::cell::Cell::new(0.0f64);
let t_sample = std::cell::Cell::new(0.0f64);
let mut t_save = 0.0f64;
let phase_start = std::time::Instant::now();
for step in 0..coupled_steps {
let d_n = extract(&flag_state);
@@ -445,8 +453,10 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
extract(&predicted)
};
let save_start = std::time::Instant::now();
let fluid_saved = solver.borrow().snapshot();
let field_saved = field.borrow().clone();
t_save += save_start.elapsed().as_secs_f64();
type PassResult = (
FlowField,
DynamicState,
@@ -460,6 +470,7 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
// Subcycled fluid steps from the SAME start-of-step state,
// geometry interpolated to each substep's end time, interface
// velocity of THIS candidate constant over the step.
let fluid_start = std::time::Instant::now();
let mut solver_ref = solver.borrow_mut();
solver_ref.restore(&fluid_saved);
let mut trial_field = field_saved.clone();
@@ -471,10 +482,13 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
subcycle,
v_n.as_deref(),
);
t_fluid.set(t_fluid.get() + fluid_start.elapsed().as_secs_f64());
// Load on the candidate geometry, flag answers from the
// committed state.
let sample_start = std::time::Instant::now();
let (nodal, conservation, skipped) =
harness.sample_load(&solver_ref, &trial_field, d_candidate);
t_sample.set(t_sample.get() + sample_start.elapsed().as_secs_f64());
if step < trace_steps {
let load: f64 = nodal.iter().map(|(_, f)| f.norm()).sum();
let peak = nodal.iter().map(|(_, f)| f.norm()).fold(0.0, f64::max);
@@ -484,9 +498,11 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
d_candidate.iter().map(|v| v * v).sum::<f64>().sqrt()
);
}
let structure_start = std::time::Instant::now();
let mut flag_ref = flag.borrow_mut();
flag_ref.set_nodal_forces(&nodal);
let (candidate_state, _) = flag_ref.step(&flag_state).unwrap();
t_structure.set(t_structure.get() + structure_start.elapsed().as_secs_f64());
let d_new = extract(&candidate_state);
if step < trace_steps {
let residual: f64 = d_new
@@ -635,6 +651,21 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
}
}
let coupled_elapsed = phase_start.elapsed().as_secs_f64();
let (f, s, l) = (t_fluid.get(), t_structure.get(), t_sample.get());
let pct = |x: f64| 100.0 * x / coupled_elapsed.max(1e-9);
println!(
" wall split over the coupled phase: fluid {f:.0} s ({:.1}%), structure {s:.1} s \
({:.2}%), load sampling {l:.0} s ({:.1}%), state save {t_save:.0} s ({:.1}%), \
other {:.0} s ({:.1}%)",
pct(f),
pct(s),
pct(l),
pct(t_save),
coupled_elapsed - f - s - l - t_save,
pct(coupled_elapsed - f - s - l - t_save),
);
MarchResult {
dt,
coupled_steps,