driver: lay some scaffolding to allow us to optimize the stimulus in future

This commit is contained in:
2022-08-18 22:19:50 -07:00
parent ffda00b796
commit 35dbdffda7
2 changed files with 96 additions and 16 deletions

View File

@@ -43,7 +43,7 @@ use coremem::meas;
use coremem::real::{self, Real as _};
use coremem::sim::spirv::{self, SpirvSim};
use coremem::sim::units::Seconds;
use coremem::stim::{CurlVectorField, Exp, Gated, ModulatedVectorField, Shifted, StimExt as _, StimuliVec};
use coremem::stim::{CurlVectorField, Exp, Gated, ModulatedVectorField, Shifted, StimExt as _};
use coremem::Driver;
type R = real::R32;
@@ -248,7 +248,6 @@ fn main() {
})
.collect();
assert_eq!(stim.len(), 5);
let stim = StimuliVec::from_vec(stim);
let wire_mat = IsomorphicConductor::new(1e6f32.cast::<R>());
@@ -304,7 +303,10 @@ fn main() {
let duration = Seconds(params.clock_phase_duration * (num_cycles + 3) as f32);
// let stim = DynStimuli::from_vec(stim.map(MapIntoBoxStimulus).into_vec());
let mut driver = driver.with_stimulus(stim);
let mut driver = driver.with_concrete_stimulus();
for s in stim {
driver.add_stimulus(s);
}
let prefix = "out/applications/multi_core_inverter/20-4ns-1e7A/";
let _ = std::fs::create_dir_all(&prefix);

View File

@@ -6,8 +6,9 @@ use crate::real::Real;
use crate::render::{self, MultiRenderer, Renderer};
use crate::sim::AbstractSim;
use crate::sim::units::{Frame, Time};
use crate::stim::{Stimulus, DynStimuli};
use crate::stim::{DynStimuli, Stimulus, StimuliVec};
use coremem_cross::compound::list;
use coremem_cross::step::SimMeta;
use log::{info, trace};
use serde::{Deserialize, Serialize};
@@ -17,7 +18,7 @@ use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
use std::time::Instant;
use threadpool::ThreadPool;
pub struct Driver<S, Stim=DynStimuli> {
pub struct Driver<S, Stim=DriverStimulusDynVec> {
state: S,
renderer: Arc<MultiRenderer<S>>,
// TODO: use Rayon's thread pool?
@@ -56,26 +57,32 @@ impl<S: AbstractSim, Stim> Driver<S, Stim> {
pub fn add_measurement<Meas: AbstractMeasurement<S> + 'static>(&mut self, m: Meas) {
self.measurements.push(Arc::new(m));
}
pub fn add_stimulus<SNew>(&mut self, s: SNew)
where Stim: Pushable<SNew>
{
self.stimuli.push(s)
}
}
impl<S: AbstractSim> Driver<S, DynStimuli> {
impl<S: AbstractSim> Driver<S, DriverStimulusDynVec> {
pub fn new(state: S) -> Self {
Self::new_with_stim(state, DynStimuli::default())
}
pub fn add_stimulus<Stim: Stimulus + 'static>(&mut self, s: Stim) {
self.stimuli.push(Box::new(s))
Self::new_with_stim(state, DriverStimulusDynVec::default())
}
}
impl<S: AbstractSim> Driver<S, list::Empty> {
pub fn new_unboxed_stim(state: S) -> Self {
pub fn new_list_stim(state: S) -> Self {
Self::new_with_stim(state, list::Empty::default())
}
}
impl<S: AbstractSim, Stim> Driver<S, Stim> {
pub fn with_add_stimulus<E>(self, s: E) -> Driver<S, list::Prepended<E, Stim>>
where Stim: list::Prependable
/// add a stimulus onto a list of non-monomorphized stimuli.
/// this necessarily must return a new Self.
/// (well, with enough tuning we could actually Box just the first reference...
pub fn with_add_stimulus<E>(self, s: E) -> Driver<S, list::Appended<Stim, E>>
where Stim: list::Appendable<E>
{
Driver {
state: self.state,
@@ -83,7 +90,7 @@ impl<S: AbstractSim, Stim> Driver<S, Stim> {
render_pool: self.render_pool,
render_channel: self.render_channel,
measurements: self.measurements,
stimuli: self.stimuli.prepend(s),
stimuli: self.stimuli.append(s),
sim_end_time: self.sim_end_time,
diag: self.diag,
last_diag_time: self.last_diag_time,
@@ -102,6 +109,9 @@ impl<S: AbstractSim, Stim> Driver<S, Stim> {
last_diag_time: self.last_diag_time,
}
}
pub fn with_concrete_stimulus<T>(self) -> Driver<S, DriverStimulusVec<T>> {
self.with_stimulus(DriverStimulusVec::<T>::default())
}
}
impl<S: AbstractSim, Stim> Driver<S, Stim> {
@@ -199,7 +209,7 @@ where
impl<S, Stim> Driver<S, Stim>
where
S: AbstractSim + Clone + Default + Send + 'static,
Stim: Stimulus,
Stim: DriverStimulus,
{
fn render(&mut self) {
self.diag.instrument_render_prep(|| {
@@ -238,7 +248,8 @@ where
}
trace!("step begin");
self.diag.instrument_step(can_step as u64, || {
self.state.step_multiple(can_step, &self.stimuli);
let stim = self.stimuli.optimized_for(self.state.meta());
self.state.step_multiple(can_step, stim.as_ref());
});
trace!("step end");
if self.last_diag_time.elapsed().as_secs_f64() >= 5.0 {
@@ -290,3 +301,70 @@ where
self.sim_end_time = None;
}
}
pub enum ValueOrRef<'a, T> {
Value(T),
Ref(&'a T),
}
impl<'a, T> AsRef<T> for ValueOrRef<'a, T> {
fn as_ref(&self) -> &T {
match self {
ValueOrRef::Value(x) => &x,
ValueOrRef::Ref(x) => x,
}
}
}
/// gives an opportunity to optimize a Stimulus for a specific setting
/// before passing it off to the simulation.
pub trait DriverStimulus {
type Optimized: Stimulus;
fn optimized_for<'a>(
&'a self, meta: SimMeta<f32>
) -> ValueOrRef<'a, Self::Optimized>;
}
pub trait Pushable<T> {
fn push(&mut self, t: T);
}
pub struct DriverStimulusVec<T>(StimuliVec<T>);
impl<T> Default for DriverStimulusVec<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T: Stimulus> DriverStimulus for DriverStimulusVec<T> {
type Optimized = StimuliVec<T>;
fn optimized_for<'a>(
&'a self, _meta: SimMeta<f32>
) -> ValueOrRef<'a, Self::Optimized> {
ValueOrRef::Ref(&self.0)
}
}
impl<T> Pushable<T> for DriverStimulusVec<T> {
fn push(&mut self, t: T) {
self.0.push(t)
}
}
#[derive(Default)]
pub struct DriverStimulusDynVec(DynStimuli);
impl DriverStimulus for DriverStimulusDynVec {
type Optimized = DynStimuli;
fn optimized_for<'a>(
&'a self, _meta: SimMeta<f32>
) -> ValueOrRef<'a, Self::Optimized> {
ValueOrRef::Ref(&self.0)
}
}
impl<T: Stimulus + 'static> Pushable<T> for DriverStimulusDynVec {
fn push(&mut self, t: T) {
self.0.push(Box::new(t))
}
}