embedded3 PERF-3 P1-1: the moving path's host work, digit-identical — impose_from's solid faces by z plane in parallel (1.95 -> 0.62 s per step at 11.6 M cells), the f32 fine export derived from the f64 level the refresh already builds (export_fine 1.04 -> 0.34 s); sub-laps in the mask rebuild; rebuild block 9.4 -> 7.4 s
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
Documentation / Build User Guide (push) Successful in 5s
CI / Build (macos-latest) (push) Waiting to run
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
Documentation / Build API Documentation (push) Failing after 7s
CI / Build CPU-Only (Explicit) (push) Failing after 8s
CI / Format Check (push) Failing after 12s
CI / Build (ubuntu-latest) (push) Failing after 1m51s
CI / Clippy Check (push) Failing after 2m5s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m45s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-19 15:59:19 -05:00
co-authored by Claude Fable 5.1
parent 103e2576ae
commit 40268ad19b
4 changed files with 102 additions and 46 deletions
@@ -5,6 +5,7 @@
use super::body::Body;
use super::wall::{FaceKind, Mask};
use rayon::prelude::*;
/// The imposition band of a cut mask, in cells.
pub(crate) const IMPOSE_BAND_CELLS: f64 = 4.0;
@@ -52,12 +53,19 @@ impl Mask {
Some(c) => (&c.d_u, &c.d_v, &c.d_w),
None => (&[], &[], &[]),
};
for k in 0..nz {
// One z plane per task: every solid face is written from the body's
// surface velocity alone (no reduction), so the parallel loop is
// digit-identical to the serial one (PERF-3 P1-1: 1.95 s of a 9.4 s
// step at 11.6 M cells, serial).
u.par_chunks_mut(ny * (nx + 1))
.take(nz)
.enumerate()
.for_each(|(k, plane)| {
for j in 0..ny {
for i in 1..nx {
let idx = g.uface(k, j, i);
if self.u_kind[idx] == FaceKind::Solid && near(d_u, idx) {
u[idx] = body
plane[j * (nx + 1) + i] = body
.surface_velocity(
i as f64 * dx,
(j as f64 + 0.5) * dy,
@@ -68,11 +76,16 @@ impl Mask {
}
}
}
});
v.par_chunks_mut((ny + 1) * nx)
.take(nz)
.enumerate()
.for_each(|(k, plane)| {
for j in 1..ny {
for i in 0..nx {
let idx = g.vface(k, j, i);
if self.v_kind[idx] == FaceKind::Solid && near(d_v, idx) {
v[idx] = body
plane[j * nx + i] = body
.surface_velocity(
(i as f64 + 0.5) * dx,
j as f64 * dy,
@@ -83,13 +96,16 @@ impl Mask {
}
}
}
}
for k in 0..=nz {
});
w.par_chunks_mut(ny * nx)
.take(nz + 1)
.enumerate()
.for_each(|(k, plane)| {
for j in 0..ny {
for i in 0..nx {
let idx = g.wface(k, j, i);
if self.w_kind[idx] == FaceKind::Solid && near(d_w, idx) {
w[idx] = body
plane[j * nx + i] = body
.surface_velocity(
(i as f64 + 0.5) * dx,
(j as f64 + 0.5) * dy,
@@ -100,7 +116,7 @@ impl Mask {
}
}
}
}
});
let u_vals: Vec<f64> = self
.u_ghosts
.iter()
@@ -255,7 +255,7 @@ impl DeviceCg {
let l_key = lap.elapsed();
// The V-cycle's finest level follows the operator (its coarser
// levels stay): the fine level alone, no hierarchy build.
let fine_level = super::export::export_fine(problem);
let fine_level = super::export::export_fine_from(&fine);
let l_export = lap.elapsed();
self.vcycle.refresh_fine(&fine_level);
if profile {
@@ -125,6 +125,40 @@ pub fn export_fine(problem: &Problem) -> LevelExport {
}
}
/// As [`export_fine`], from the f64 fine level a caller already built
/// (PERF-3 P1-1): the f32 level is the same masked coefficients cast, so this
/// is the identical export without a second clone of the problem and a
/// second level build.
pub fn export_fine_from(lv: &Level<f64>) -> LevelExport {
let (_, coarse_of) = lv.coarsen();
let to_u32 = |v: &[usize]| {
v.iter()
.map(|&i| if i == usize::MAX { u32::MAX } else { i as u32 })
.collect::<Vec<u32>>()
};
let cast = |v: &[f64]| v.iter().map(|&x| x as f32).collect::<Vec<f32>>();
LevelExport {
nx: lv.problem.nx,
ny: lv.problem.ny,
nz: lv.problem.nz,
cells: to_u32(&lv.cells),
red: to_u32(&lv.red),
black: to_u32(&lv.black),
top: to_u32(&lv.top),
bot: to_u32(&lv.bot),
coarse_of: to_u32(&coarse_of),
children_ptr: Vec::new(),
children_idx: Vec::new(),
ae: cast(&lv.ae),
aw: cast(&lv.aw),
an: cast(&lv.an),
as_: cast(&lv.as_),
at: cast(&lv.at),
ab: cast(&lv.ab),
ap: cast(&lv.ap),
}
}
/// `z = M⁻¹ r` by the host f32 V-cycle: the reference a device V-cycle is measured against.
pub fn vcycle_f32_reference(
problem: &Problem,
@@ -51,6 +51,7 @@ impl Solver {
}
}
let l_step = lap.elapsed();
let sub = std::time::Instant::now();
new_mask.impose_from(
body,
&field.u_old,
@@ -61,6 +62,7 @@ impl Solver {
&mut field.w,
t_new,
);
let s_impose = sub.elapsed();
if new_mask.cut().is_some() {
let (table, correction) = match &self.mask {
Some(old_mask) => new_mask.gcl_flux_table(old_mask, dt),
@@ -69,6 +71,7 @@ impl Solver {
self.wall_fluxes = table;
self.last_ghost_correction = correction;
}
let s_gcl = sub.elapsed();
if let Some(old) = &self.mask {
self.apertures_old = old
.cut()
@@ -86,10 +89,13 @@ impl Solver {
if std::env::var("RTX_E3_MOVING_PROFILE").is_ok() {
let ms = |d: std::time::Duration| d.as_secs_f64() * 1e3;
eprintln!(
" mask laps: build_mask {:.0} ms, refill + step apertures + merging {:.0} ms, impose + GCL + volumes {:.0} ms",
" mask laps: build_mask {:.0} ms, refill + step apertures + merging {:.0} ms, impose + GCL + volumes {:.0} ms (impose {:.0}, GCL table {:.0}, old apertures + volumes {:.0})",
ms(l_build),
ms(l_step - l_build),
ms(lap.elapsed() - l_step)
ms(lap.elapsed() - l_step),
ms(s_impose),
ms(s_gcl - s_impose),
ms(sub.elapsed() - s_gcl)
);
}
self.mask = Some(new_mask);