From 6362b5a60ee9d90c22d2e6a691e33f69efb0bcaa Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 15 Dec 2020 14:19:29 -0800 Subject: [PATCH] parallelize render_scalar_field --- Cargo.toml | 2 ++ examples/wrapped_torus.rs | 6 ++-- src/geom/mod.rs | 2 +- src/meas.rs | 2 +- src/render.rs | 59 +++++++++++++++++++++------------------ 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a6a77aa..7d94d88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,10 @@ lazy_static = "1.4" log = "0.4" natord = "1.0" ndarray = { version = "0.13", features = ["rayon", "serde"] } +num = "0.3" piecewise-linear = "0.1" plotly = { version = "0.6", features = ["kaleido", "plotly_ndarray"], path = "../plotly/plotly" } +rayon = "1.5" serde = "1.0" structopt = "0.3" threadpool = "1.8" diff --git a/examples/wrapped_torus.rs b/examples/wrapped_torus.rs index 7f46e56..3447d2d 100644 --- a/examples/wrapped_torus.rs +++ b/examples/wrapped_torus.rs @@ -5,7 +5,7 @@ use coremem::stim::{CurlStimulus, Sinusoid1, TimeVarying1 as _}; fn main() { coremem::init_logging(); let feat_size = 10e-6; // feature size - let duration = 2e-9; + let duration = 3e-9; let width = 2200e-6; let depth = 1800e-6; let buffer = 200e-6; @@ -13,8 +13,8 @@ fn main() { let ferro_minor = 40e-6; let wire_minor = 20e-6; let wire_major = 100e-6; - let peak_current = 1e4; - let current_duration = 0.5e-9; // half-wavelength of the sine wave + let peak_current = 5e4; + let current_duration = 1.0e-9; // half-wavelength of the sine wave let current_break = 0.5e-9; // time between 'set' pulse and 'clear' pulse let conductivity = 1.0e9; diff --git a/src/geom/mod.rs b/src/geom/mod.rs index a5daa26..e8aaa6e 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -184,7 +184,7 @@ impl Polygon2d { } #[typetag::serde(tag = "type")] -pub trait Region: Send + DynClone { +pub trait Region: Send + Sync + DynClone { fn contains(&self, p: Meters) -> bool; } dyn_clone::clone_trait_object!(Region); diff --git a/src/meas.rs b/src/meas.rs index 084c954..3e51acc 100644 --- a/src/meas.rs +++ b/src/meas.rs @@ -9,7 +9,7 @@ use std::iter::Sum; // TODO: remove this Clone and Send requirement? Have Measurements be shared by-reference across // threads? i.e. Sync, and no Clone #[typetag::serde(tag = "type")] -pub trait AbstractMeasurement: Send + DynClone { +pub trait AbstractMeasurement: Send + Sync + DynClone { fn eval(&self, state: &dyn GenericSim) -> String; } dyn_clone::clone_trait_object!(AbstractMeasurement); diff --git a/src/render.rs b/src/render.rs index 8e936d5..f23e9ac 100644 --- a/src/render.rs +++ b/src/render.rs @@ -7,9 +7,11 @@ use crossterm::{cursor, QueueableCommand as _}; use crossterm::style::{style, Color, PrintStyledContent}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use log::trace; +use num::integer::Integer; use plotly; use image::{RgbImage, Rgb}; use imageproc::{pixelops, drawing}; +use rayon::prelude::*; use serde::{Serialize, Deserialize}; use std::fs::File; use std::io::{BufWriter, Write as _}; @@ -158,36 +160,39 @@ impl<'a> RenderSteps<'a> { let w = self.im.width(); let h = self.im.height(); let vec_spacing = 10; - for y in 0..h { - for x in 0..w { - if x % vec_spacing == 0 && y % vec_spacing == 0 { - let vec = self.field_vector(x, y, vec_spacing, &measure); - let norm_vec = scale_vector(vec, typical); - let alpha = 0.7*scale_unsigned(vec.mag_sq(), typical * 5.0); - let vec = norm_vec * (vec_spacing as Flt); - let center = Vec2::new(x as _, y as _) + Vec2::new(vec_spacing as _, vec_spacing as _)*0.5; - self.im.draw_field_arrow(center, vec, color, alpha as f32); - } + for y in (0..h).into_iter().step_by(vec_spacing as _) { + for x in (0..w).into_iter().step_by(vec_spacing as _) { + let vec = self.field_vector(x, y, vec_spacing, &measure); + let norm_vec = scale_vector(vec, typical); + let alpha = 0.7*scale_unsigned(vec.mag_sq(), typical * 5.0); + let vec = norm_vec * (vec_spacing as Flt); + let center = Vec2::new(x as _, y as _) + Vec2::new(vec_spacing as _, vec_spacing as _)*0.5; + self.im.draw_field_arrow(center, vec, color, alpha as f32); } } } - fn render_scalar_field) -> Flt>(&mut self, typical: Flt, signed: bool, slot: u32, measure: F) { - let w = self.im.width(); - let h = self.im.height(); - for y in 0..h { - for x in 0..w { - let cell = self.get_at_px(x, y); - let value = measure(&cell); - let scaled = if signed { - scale_signed_to_u8(value, typical) - } else { - scale_unsigned_to_u8(value, typical) - }; - let mut p = *self.im.get_pixel(x, y); - p.0[slot as usize] = scaled; - self.im.put_pixel(x, y, p); - } - } + fn render_scalar_field) -> Flt + Sync>(&mut self, typical: Flt, signed: bool, slot: u32, measure: F) { + // XXX: get_at_px borrows self, so we need to clone the image to operate on it mutably. + let mut im = self.im.clone(); + let w = im.width(); + let h = im.height(); + let samples = im.as_flat_samples_mut(); + assert_eq!(samples.layout.channel_stride, 1); + assert_eq!(samples.layout.width_stride, 3); + assert_eq!(samples.layout.height_stride, 3*w as usize); + let pixel_buf: &mut [[u8; 3]] = unsafe { std::mem::transmute(samples.samples) }; + pixel_buf[..(w*h) as usize].par_iter_mut().enumerate().for_each(|(idx, px)| { + let (y, x) = (idx as u32).div_rem(&w); + let cell = self.get_at_px(x, y); + let value = measure(&cell); + let scaled = if signed { + scale_signed_to_u8(value, typical) + } else { + scale_unsigned_to_u8(value, typical) + }; + px[slot as usize] = scaled; + }); + self.im = im; } fn render_measurements(&mut self) { for (meas_no, m) in self.meas.iter().enumerate() {