From 845e0ae01a7b746150adc84557e1fd74be6d5ad1 Mon Sep 17 00:00:00 2001 From: Omar Sobh Date: Sat, 19 Sep 2026 18:42:55 -0500 Subject: [PATCH] embedded3 PERF-3 P1-2: the flag test's centreline polyline cached per thread and time (the 41-point rebuild with four transcendental evaluations each ran per surface-velocity call, ~10^6 calls per step): table faces 1,620 -> 54 ms, impose 622 -> 12 ms, build_mask 898 -> 404 ms, rebuild block 6.9 -> 4.3 s per step at 11.6 M cells; slab CSV byte-identical; table sub-laps Co-Authored-By: Claude Fable 5.1 --- .../embedded3/step/device/cut.rs | 21 +++++++++++-- .../rtx-cfd/tests/embedded3_flag_wake.rs | 30 ++++++++++++++----- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device/cut.rs b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device/cut.rs index 79d9fec..9b84161 100644 --- a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device/cut.rs +++ b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device/cut.rs @@ -82,7 +82,7 @@ pub(super) struct DeviceCut { /// Which phase the tables serve: the predictor reads the instantaneous /// apertures and kinds of the mask at its time; the projection reads the /// step-averaged apertures and the space-time classification. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub(super) enum Phase { Predictor, Projection, @@ -125,6 +125,8 @@ impl DeviceCut { }; // Surface velocity at the foot per face within the imposition band // (zero beyond it: never read), and the open flags. + let lap = Instant::now(); + let profile = std::env::var("RTX_E3_MOVING_PROFILE").is_ok(); let band = mask.impose_band().unwrap_or(f64::INFINITY); let dists: [&[f64]; 3] = [&cut.d_u, &cut.d_v, &cut.d_w]; let mut ub: [Vec; 3] = [Vec::new(), Vec::new(), Vec::new()]; @@ -176,6 +178,7 @@ impl DeviceCut { ub[c] = ubc; open[c] = opc; } + let l_faces = lap.elapsed(); // The solver's current table (the GCL table on a moving body) when // it has one, else the static porous table. let wall_flux: Vec = if solver.wall_fluxes().len() == g.cells() { @@ -227,9 +230,10 @@ impl DeviceCut { cursor[m] += 1; } } + let l_cells = lap.elapsed(); let ub_host = ub; let ub_dev = [up_f(&ub_host[0]), up_f(&ub_host[1]), up_f(&ub_host[2])]; - Some(Self { + let built = Self { ub_host, a: [up_f(ap_u), up_f(ap_v), up_f(ap_w)], d: [up_f(&cut.d_u), up_f(&cut.d_v), up_f(&cut.d_w)], @@ -246,7 +250,18 @@ impl DeviceCut { None => [up_f(&[0.0]), up_f(&[0.0]), up_f(&[0.0])], }, merged, - }) + }; + if profile { + let ms = |d: std::time::Duration| d.as_secs_f64() * 1e3; + eprintln!( + " table laps ({:?}): faces {:.0} ms, cells {:.0} ms, uploads {:.0} ms", + phase, + ms(l_faces), + ms(l_cells - l_faces), + ms(lap.elapsed() - l_cells) + ); + } + Some(built) } /// The PREDICTOR tables from the PROJECTION tables of the same mask and diff --git a/crates/specialized/rtx-cfd/tests/embedded3_flag_wake.rs b/crates/specialized/rtx-cfd/tests/embedded3_flag_wake.rs index 09eb6df..d66d94e 100644 --- a/crates/specialized/rtx-cfd/tests/embedded3_flag_wake.rs +++ b/crates/specialized/rtx-cfd/tests/embedded3_flag_wake.rs @@ -82,15 +82,31 @@ fn amplitude() -> f64 { /// around the centreline polyline of `n` segments) and the centreline's /// transverse velocity at the closest point. fn flag_2d(x: f64, y: f64, t: f64) -> (f64, f64) { - let n = 40; + const N: usize = 40; + // The centreline polyline at `t`, once per thread and time (PERF-3 + // P1-2): the solver asks for the surface velocity at ~10⁶ faces per + // step and each call rebuilt the 41 points (four hyperbolic / trigonometric + // evaluations each). Same arithmetic, same digits. + thread_local! { + static POLYLINE: std::cell::RefCell<(f64, [(f64, f64, f64); N + 1])> = + const { std::cell::RefCell::new((f64::NAN, [(0.0, 0.0, 0.0); N + 1])) }; + } + let pts = POLYLINE.with(|cell| { + let mut c = cell.borrow_mut(); + if c.0.to_bits() != t.to_bits() { + for (m, p) in c.1.iter_mut().enumerate() { + let s = m as f64 / N as f64; + let (d, v) = deflection(s, t); + *p = (FLAG_X0 + s * FLAG_LEN, CY + d, v); + } + c.0 = t; + } + c.1 + }); let mut best = f64::INFINITY; let mut v_best = 0.0; - let point = |m: usize| { - let s = m as f64 / n as f64; - let (d, v) = deflection(s, t); - (FLAG_X0 + s * FLAG_LEN, CY + d, v) - }; - for m in 0..n { + let point = |m: usize| pts[m]; + for m in 0..N { let (ax, ay, av) = point(m); let (bx, by, bv) = point(m + 1); let (ex, ey) = (bx - ax, by - ay);