add some more caching around the state file & geometry calculations

able to process a simulation in ~2 seconds in the optimistic path
This commit is contained in:
2022-02-01 18:31:47 -08:00
parent f0ab5ea29d
commit 0e98f8582a
3 changed files with 86 additions and 55 deletions

View File

@@ -134,7 +134,7 @@ struct Params {
pre_time: f32, // how long between set and clock pre_time: f32, // how long between set and clock
post_time: f32, // how long to wait after the clock post_time: f32, // how long to wait after the clock
dump_frames: Option<u64>, dump_frames: (u64, Option<u64>),
} }
#[derive(Clone, Default, Serialize, Deserialize)] #[derive(Clone, Default, Serialize, Deserialize)]
@@ -337,33 +337,57 @@ fn derive_geometries(p: GeomParams) -> Option<Geometries> {
fn run_sim(id: u32, p: Params, g: Geometries) -> Results { fn run_sim(id: u32, p: Params, g: Geometries) -> Results {
info!("run_sim {}: {:?}", id, p); info!("run_sim {}: {:?}", id, p);
let m_to_um = |m: f32| (m * 1e6).round() as u32; let m_to_um = |m: f32| (m * 1e6).round() as u32;
let feat_vol = p.geom.feat_size * p.geom.feat_size * p.geom.feat_size; let feat_vol = p.geom.feat_size * p.geom.feat_size * p.geom.feat_size;
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::Ferroxcube3R1MH::new();
// let ferro_mat = mat::db::conductor(wire_conductivity);
let wire_mat = mat::IsomorphicConductor::new(p.wire_conductivity);
let mut driver: SpirvDriver<Mat> = Driver::new_spirv(g.dim, p.geom.feat_size);
driver.set_steps_per_stim(1000);
driver.fill_region(&g.ferro1_region, ferro_mat);
driver.fill_region(&g.ferro2_region, ferro_mat);
driver.fill_region(&g.set1_region, wire_mat);
driver.fill_region(&g.set2_region, wire_mat);
driver.fill_region(&g.coupling_region, wire_mat);
info!("boundary: {}um; {}um", m_to_um(p.geom.boundary_xy), m_to_um(p.geom.boundary_z)); info!("boundary: {}um; {}um", m_to_um(p.geom.boundary_xy), m_to_um(p.geom.boundary_z));
info!("size: {:?}", g.dim); info!("size: {:?}", g.dim);
info!("ferro1: {:?}", g.ferro1_region.center()); info!("ferro1: {:?}", g.ferro1_region.center());
info!("ferro2: {:?}", g.ferro2_region.center()); info!("ferro2: {:?}", g.ferro2_region.center());
driver.add_classical_boundary(Meters::new(p.geom.boundary_xy, p.geom.boundary_xy, p.geom.boundary_z));
// assert!(driver.test_region_filled(&g.ferro1_region, ferro_mat)); let base = format!("buffer5-{}", id);
// assert!(driver.test_region_filled(&g.ferro2_region, ferro_mat)); let prefix = format!("out/{}/{}-{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um-{}ferromaj-{}:{}wraps-{}:{}cov-{:?}clk",
// assert!(driver.test_region_filled(&g.set1_region, wire_mat)); base,
// assert!(driver.test_region_filled(&g.set2_region, wire_mat)); base,
// assert!(driver.test_region_filled(&g.coupling_region, wire_mat)); *g.dim.to_index(p.geom.feat_size),
(p.peak_set_current * 1e3).round() as i64,
(p.set_duration * 1e12).round() as i64,
(p.peak_clock_current * 1e3).round() as i64,
(p.clock_duration * 1e12).round() as i64,
(p.geom.feat_size * 1e6).round() as i64,
p.geom.ferro_major,
p.geom.wraps1,
p.geom.wraps2,
p.geom.wrap1_coverage,
p.geom.wrap2_coverage,
p.clock_type,
);
let mut driver: SpirvDriver<Mat> = Driver::new_spirv(g.dim, p.geom.feat_size);
driver.set_steps_per_stim(1000);
if !driver.add_state_file(&*format!("{}/state.bc", prefix), 16000) {
// mu_r=881.33, starting at H=25 to H=75.
let ferro_mat = mat::Ferroxcube3R1MH::new();
// let ferro_mat = mat::db::conductor(wire_conductivity);
let wire_mat = mat::IsomorphicConductor::new(p.wire_conductivity);
driver.fill_region(&g.ferro1_region, ferro_mat);
driver.fill_region(&g.ferro2_region, ferro_mat);
driver.fill_region(&g.set1_region, wire_mat);
driver.fill_region(&g.set2_region, wire_mat);
driver.fill_region(&g.coupling_region, wire_mat);
driver.add_classical_boundary(Meters::new(p.geom.boundary_xy, p.geom.boundary_xy, p.geom.boundary_z));
// assert!(driver.test_region_filled(&g.ferro1_region, ferro_mat));
// assert!(driver.test_region_filled(&g.ferro2_region, ferro_mat));
// assert!(driver.test_region_filled(&g.set1_region, wire_mat));
// assert!(driver.test_region_filled(&g.set2_region, wire_mat));
// assert!(driver.test_region_filled(&g.coupling_region, wire_mat));
} else {
info!("loaded state file: skipping geometry calculations");
}
let add_drive_sine_pulse = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| { let add_drive_sine_pulse = |driver: &mut SpirvDriver<Mat>, region: &Torus, start: f32, duration: f32, amp: f32| {
let wave = Sinusoid1::from_wavelength(amp, duration * 2.0) let wave = Sinusoid1::from_wavelength(amp, duration * 2.0)
@@ -445,24 +469,6 @@ fn run_sim(id: u32, p: Params, g: Geometries) -> Results {
driver.add_measurement(meas::Current::new("couplingtop", g.coupling_wire_top.clone())); driver.add_measurement(meas::Current::new("couplingtop", g.coupling_wire_top.clone()));
driver.add_measurement(meas::Current::new("couplingbot", g.coupling_wire_bot.clone())); driver.add_measurement(meas::Current::new("couplingbot", g.coupling_wire_bot.clone()));
let base = format!("buffer5-{}", id);
let prefix = format!("out/{}/{}-{}-{}setmA-{}setps-{}clkmA-{}clkps-{}um-{}ferromaj-{}:{}wraps-{}:{}cov-{:?}clk",
base,
base,
*driver.size(),
(p.peak_set_current * 1e3).round() as i64,
(p.set_duration * 1e12).round() as i64,
(p.peak_clock_current * 1e3).round() as i64,
(p.clock_duration * 1e12).round() as i64,
(p.geom.feat_size * 1e6).round() as i64,
p.geom.ferro_major,
p.geom.wraps1,
p.geom.wraps2,
p.geom.wrap1_coverage,
p.geom.wrap2_coverage,
p.clock_type,
);
if p.dry_run { if p.dry_run {
info!("bailing (dry run): {}", prefix); info!("bailing (dry run): {}", prefix);
return Results::default(); return Results::default();
@@ -470,8 +476,8 @@ fn run_sim(id: u32, p: Params, g: Geometries) -> Results {
let _ = std::fs::create_dir_all(&prefix); let _ = std::fs::create_dir_all(&prefix);
driver.add_state_file(&*format!("{}/state.bc", prefix), 16000); let (frame_freq, frame_limit) = p.dump_frames;
driver.add_serializer_renderer(&*format!("{}/frame-", prefix), 32000, p.dump_frames); driver.add_serializer_renderer(&*format!("{}/frame-", prefix), frame_freq, frame_limit);
let meas_csv = format!("{}/meas.csv", prefix); let meas_csv = format!("{}/meas.csv", prefix);
let meas_sparse_csv = format!("{}/meas-sparse.csv", prefix); let meas_sparse_csv = format!("{}/meas-sparse.csv", prefix);
driver.add_csv_renderer(&*meas_csv, 400, None); driver.add_csv_renderer(&*meas_csv, 400, None);
@@ -526,23 +532,24 @@ fn main() {
// for (wrap1_cov, wrap2_cov) in [(0.8, 0.8), (0.8, 0.25), (0.25, 0.8)] { // for (wrap1_cov, wrap2_cov) in [(0.8, 0.8), (0.8, 0.25), (0.25, 0.8)] {
let clock_domain = [ let clock_domain = [
(25600.0, 5.0 * ns), (25600.0, 5.0 * ns),
(6400.0, 5.0 * ns),
// (1600.0, 5.0 * ns), // low relevance for large cores // (1600.0, 5.0 * ns), // low relevance for large cores
// (1600.0, 1.0 * ns), // very poor perf (0.05 m2_stable_m1_peak) // (1600.0, 1.0 * ns), // very poor perf (0.05 m2_stable_m1_peak)
(25600.0, 1.0 * ns), (25600.0, 1.0 * ns),
// (6400.0, 1.0 * ns), // low relevance for large cores // (6400.0, 1.0 * ns), // low relevance for large cores
// (51200.0, 1.0 * ns), // (51200.0, 1.0 * ns),
(102400.0, 1.0 * ns), (102400.0, 1.0 * ns),
// (409600.0, 1.0 * ns), // I(set1) shows significant underdamping => bad m2_stable_m1_peak
(12800.0, 5.0 * ns),
(51200.0, 5.0 * ns), (51200.0, 5.0 * ns),
(12800.0, 5.0 * ns),
// (400.0, 25.0 * ns), // poor perf (0.28 m2_stable_m1_peak)
// (1600.0, 25.0 * ns), // mediocre perf (0.65 m2_stable_m1_peak)
// TODO: RE-ENABLE // TODO: RE-ENABLE
// (6400.0, 25.0 * ns), // suspended because costly // (6400.0, 25.0 * ns), // suspended because costly
(12800.0, 25.0 * ns), // suspended because costly
// (25600.0, 25.0 * ns), // suspended because costly // (25600.0, 25.0 * ns), // suspended because costly
// (6400.0, 5.0 * ns), // low relevance for large cores (for >= 9mm rad, <0.5 m2_stable_m1_peak)
// (409600.0, 1.0 * ns), // I(set1) shows significant underdamping => bad m2_stable_m1_peak
// (400.0, 25.0 * ns), // poor perf (0.28 m2_stable_m1_peak)
// (1600.0, 25.0 * ns), // mediocre perf (0.65 m2_stable_m1_peak)
// (1600.0, 100.0 * ns), // (1600.0, 100.0 * ns),
// (6400.0, 100.0 * ns), // (6400.0, 100.0 * ns),
@@ -566,8 +573,10 @@ fn main() {
// 1680.0 * um // 1680.0 * um
]; ];
let wrap2_densities = [ let wrap2_densities = [
-0.05,
-0.15, -0.15,
-0.3, -0.3,
-0.5,
// 0.3, // 0.3,
// 0.2, // 0.2,
// 0.15, // 0.15,
@@ -613,6 +622,12 @@ fn main() {
} }
} }
} }
info!(
"evaluating {} variants ({} time increments of {} primitives)",
variants.len(),
post_times.len(),
variants.len() / post_times.len(),
);
let mut geom_cache = DiskCache::new_with_supplier( let mut geom_cache = DiskCache::new_with_supplier(
&format!("out/buffer5-{}/.geom_cache", i), &format!("out/buffer5-{}/.geom_cache", i),
@@ -656,7 +671,7 @@ fn main() {
pre_time: 1e-9, pre_time: 1e-9,
post_time, post_time,
dump_frames: Some(256000), dump_frames: (256000, Some(257000)),
}; };
let wraps1_choices: Vec<_> = (-120..120) let wraps1_choices: Vec<_> = (-120..120)
@@ -712,7 +727,7 @@ fn main() {
let mut params = base_params; let mut params = base_params;
params.geom.wraps1 = wraps1; params.geom.wraps1 = wraps1;
params.geom.wraps2 = wraps2; params.geom.wraps2 = wraps2;
match derive_geometries(params.geom.clone()) { match geom_cache.get_or_insert_from_supplier(params.geom.clone()) {
Some(geoms) => { Some(geoms) => {
run_sim(i, params, geoms); run_sim(i, params, geoms);
}, },

View File

@@ -147,12 +147,19 @@ impl<S: SampleableSim + Send + Sync + Serialize + 'static> Driver<S> {
} }
impl<S: SampleableSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'static> Driver<S> { impl<S: SampleableSim + Send + Sync + Serialize + for<'a> Deserialize<'a> + 'static> Driver<S> {
pub fn add_state_file(&mut self, state_file: &str, snapshot_frequency: u64) { /// instruct the driver to periodically save the simulation state to the provided path.
/// also attempts to load an existing state file, returning `true` on success.
pub fn add_state_file(&mut self, state_file: &str, snapshot_frequency: u64) -> bool {
let ser = render::SerializerRenderer::new(state_file); let ser = render::SerializerRenderer::new(state_file);
if let Some(state) = ser.try_load() { let loaded = match ser.try_load() {
self.state = state.state; Some(state) => {
} self.state = state.state;
true
},
None => false,
};
self.add_renderer(ser, state_file, snapshot_frequency, None); self.add_renderer(ser, state_file, snapshot_frequency, None);
loaded
} }
} }
@@ -192,7 +199,7 @@ impl<S: GenericSim + Clone + Default + Send + Sync + 'static> Driver<S> {
self.render(); self.render();
} }
let mut can_step = at_most; let mut can_step = at_most; // TODO: typo?? should be `0`, or `1`?
while can_step < at_most && !self.renderer.any_work_for_frame(start_step + can_step as u64) { while can_step < at_most && !self.renderer.any_work_for_frame(start_step + can_step as u64) {
can_step += 1; can_step += 1;
} }
@@ -242,6 +249,13 @@ impl<S: GenericSim + Clone + Default + Send + Sync + 'static> Driver<S> {
self.step_multiple(1); self.step_multiple(1);
} }
/// Returns the number of timesteps needed to reach the end time
pub fn steps_until<T: Time>(&mut self, sim_end_time: T) -> u64 {
let sim_end_step = sim_end_time.to_frame(self.state.timestep());
let start_step = self.state.step_no();
sim_end_step.saturating_sub(start_step)
}
pub fn step_until<T: Time>(&mut self, sim_end_time: T) { pub fn step_until<T: Time>(&mut self, sim_end_time: T) {
let sim_end_time = sim_end_time.to_frame(self.state.timestep()); let sim_end_time = sim_end_time.to_frame(self.state.timestep());
self.sim_end_time = Some(sim_end_time); self.sim_end_time = Some(sim_end_time);

View File

@@ -561,6 +561,8 @@ impl<S: SampleableSim> Renderer<S> for MultiRenderer<S> {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct SerializedFrame<S=StaticSim> { pub struct SerializedFrame<S=StaticSim> {
pub state: S, pub state: S,
/// although not generally necessary to load the sim, saving the measurements is beneficial for
/// post-processing.
pub measurements: Vec<Box<dyn AbstractMeasurement>>, pub measurements: Vec<Box<dyn AbstractMeasurement>>,
} }
@@ -588,8 +590,8 @@ impl SerializerRenderer {
} }
} }
/// Same as `new`, but cast to StaticSim before serializing. This tends to result in a smaller /// Same as `new`, but cast to StaticSim before serializing. This yields a file that's easier
/// file. /// for post-processing, and may be smaller in size.
pub fn new_static(fmt_str: &str) -> Self { pub fn new_static(fmt_str: &str) -> Self {
Self { Self {
fmt_str: fmt_str.into(), fmt_str: fmt_str.into(),