Skip to content

Commit

Permalink
Merge pull request #86 from ivanceras/pre_eval-clone
Browse files Browse the repository at this point in the history
This adds prediff feature for sauron. This is the first step towards optimization of skipping nodes for diffing by checking the App fields alone.
  • Loading branch information
ivanceras authored Mar 2, 2024
2 parents f1d56fe + cfff54d commit f30f4a4
Show file tree
Hide file tree
Showing 57 changed files with 1,095 additions and 151 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ with-ric = ["sauron-core/with-ric"]
with-raf = ["sauron-core/with-raf"]
with-interning = ["sauron-core/with-interning"]
with-jss = ["sauron-macro", "with-lookup"] #enable use of jss (css style in json format)
prediff = ["sauron-core/prediff"]

# shows telemetry such as duration it took to update the dom
with-measure = ["sauron-core/with-measure"]
Expand Down
6 changes: 5 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@
- [ ] Bring back CreatedNode maybe with a different name: `DomNode` which wraps the `Node` or `Element` along with it's closures from event listener
- These are then saved into the `Program` where when removed or dropped, it will also drop the associated closures with them, thereby simplifying the code.
- Right now, we are attaching a `vdom-data` attribute for nodes that have listeners

- [ ] Make use of `Arc<RwLock>` to check if can solve copying the `APP` via `transmute_copy`. ~~it didn't solve it~~
- See if there are performance penalty

## Features
- [X] Storage service (May not be needed since the user can directly use web-sys)
Expand Down Expand Up @@ -304,6 +305,9 @@
- [ ] Find a way to break up building the view into multiple frames, since view can take a long time to build
- [ ] Find a way to break up diffing the current vdom and the new vdom as they can also take a bit of long time as well.
- [ ] Add benchmark function for using CACHE_ELEMENT and not
- [ ] Make dispatch pending patches break when the animation frame timeouts, same way as dispatching pending msgs
- [ ] Check the last time the dom is updated, if it is less than 17ms, delay the dom update until 17ms has elapsed since the last update.
- [ ] Make use of [talc](https://github.com/SFBdragon/talc) allocator for faster and leaner memory

## Maintenance
- [X] Move `sauron-markdown` into it's own repo, for keeping sauron slim.
Expand Down
3 changes: 2 additions & 1 deletion crates/sauron-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ log = "0.4"
cfg-if = "1.0"
thiserror = "1.0"
doc-comment = "0.3"
mt-dom = { version = "0.59.1" }
mt-dom = { version = "0.59.2" }
#mt-dom = { path = "../../../mt-dom" }
#mt-dom = { git = "https://github.com/ivanceras/mt-dom", branch = "master" }
once_cell = "1.8"
Expand Down Expand Up @@ -117,6 +117,7 @@ with-ric = [] # use of request_idle_callback in javascript
with-raf = [] # use of request_animation_frame in javascript
with-interning = [] # use caching of strings when crossing rust to js, for faster transfer
custom_element = [] # use of register_custom_element, adding this will add the js snippets
prediff = [] # diffing optimization

[dev-dependencies]
wasm-bindgen-test = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion crates/sauron-core/src/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod task;
use cfg_if::cfg_if;

cfg_if! {if #[cfg(feature = "with-dom")] {
pub use application::{Application, Measurements};
pub use application::{Application, Measurements, PreDiff, diff_if};
#[cfg(feature = "custom_element")]
pub use web_component::{register_web_component, WebComponent, WebComponentWrapper};
pub use dom_patch::{DomPatch, PatchVariant};
Expand Down
25 changes: 15 additions & 10 deletions crates/sauron-core/src/dom/application.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
use crate::dom::Cmd;
use crate::vdom::Node;
pub use prediff::{diff_if, PreDiff};

mod prediff;


/// An Application is the root component of your program.
/// Everything that happens in your application is done here.
///
pub trait Application<MSG>
pub trait Application<MSG> : Sized + 'static
where
MSG: 'static,
{
/// The application can implement this method where it can modify its initial state.
/// This method is called right after the program is mounted into the DOM.
fn init(&mut self) -> Cmd<Self, MSG>
where
Self: Sized + 'static,
{
fn init(&mut self) -> Cmd<Self, MSG> {
Cmd::none()
}

/// Update the component with a message.
/// The update function returns a Cmd, which can be executed by the runtime.
///
/// Called each time an action is triggered from the view
fn update(&mut self, _msg: MSG) -> Cmd<Self, MSG>
where
Self: Sized + 'static;
fn update(&mut self, _msg: MSG) -> Cmd<Self, MSG>;

/// an optimization solution.
/// pre evaluate the expression to determine
/// whether to diff the nodes
#[cfg(feature = "prediff")]
fn prediff(&self, _other: &Self) -> Option<Vec<PreDiff>> {
None
}

/// Returns a node on how the component is presented.
fn view(&self) -> Node<MSG>;
Expand All @@ -43,8 +50,6 @@ where
///
/// Warning: DO NOT use for anything else other than the intended purpose
fn measurements(&self, measurements: Measurements) -> Cmd<Self, MSG>
where
Self: Sized + 'static,
{
log::debug!("Measurements: {:#?}", measurements);
Cmd::none().no_render()
Expand Down
73 changes: 73 additions & 0 deletions crates/sauron-core/src/dom/application/prediff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use mt_dom::TreePath;
use std::fmt;

///
pub struct PreDiff {
expr: Box<dyn Fn() -> bool>,
children: Vec<PreDiff>,
}

impl fmt::Debug for PreDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}", (self.expr)())?;
write!(f, ",[")?;
for child in &self.children {
child.fmt(f)?;
}
write!(f, "])")?;
Ok(())
}
}

impl PreDiff {
/// new
pub fn new(val: bool, children: impl IntoIterator<Item = Self>) -> Self {
Self {
expr: Box::new(move || val),
children: children.into_iter().collect(),
}
}

///
pub fn none() -> Self {
PreDiff {
expr: Box::new(|| false),
children: vec![],
}
}

///
pub fn traverse(evals: &[PreDiff]) -> Vec<TreePath> {
let root = TreePath::root();
if evals.len() == 1 {
Self::traverse_recursive(&evals[0], root)
} else {
Self::traverse_list(evals, root)
}
}

///
fn traverse_list(evals: &[PreDiff], current: TreePath) -> Vec<TreePath> {
let mut paths = vec![];
for (i, eval) in evals.iter().enumerate() {
let more_paths = eval.traverse_recursive(current.traverse(i));
paths.extend(more_paths);
}
paths
}

fn traverse_recursive(&self, current: TreePath) -> Vec<TreePath> {
let mut paths = vec![];
if (self.expr)() {
paths.push(current.clone());
}
let more_paths = Self::traverse_list(&self.children, current);
paths.extend(more_paths);
paths
}
}

/// evaluate check
pub fn diff_if(val: bool, children: impl IntoIterator<Item = PreDiff>) -> PreDiff {
PreDiff::new(val, children)
}
8 changes: 4 additions & 4 deletions crates/sauron-core/src/dom/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ where
impl<APP, MSG> Cmd<APP, MSG>
where
MSG: 'static,
APP: Application<MSG> + 'static,
APP: Application<MSG>,
{
/// creates a new Cmd from a function
pub fn new<F>(f: F) -> Self
Expand Down Expand Up @@ -118,7 +118,7 @@ where
impl<APP, MSG> From<Effects<MSG, ()>> for Cmd<APP, MSG>
where
MSG: 'static,
APP: Application<MSG> + 'static,
APP: Application<MSG>,
{
/// Convert Effects that has only follow ups
fn from(effects: Effects<MSG, ()>) -> Self {
Expand All @@ -139,7 +139,7 @@ where
impl<APP, MSG, IN> From<IN> for Cmd<APP, MSG>
where
MSG: 'static,
APP: Application<MSG> + 'static,
APP: Application<MSG>,
IN: IntoIterator<Item = Effects<MSG, ()>>,
{
fn from(effects: IN) -> Self {
Expand All @@ -150,7 +150,7 @@ where
impl<APP, MSG> From<Task<MSG>> for Cmd<APP, MSG>
where
MSG: 'static,
APP: Application<MSG> + 'static,
APP: Application<MSG>,
{
fn from(task: Task<MSG>) -> Self {
let task = task.task;
Expand Down
2 changes: 1 addition & 1 deletion crates/sauron-core/src/dom/dom_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fn create_unique_identifier() -> usize {
impl<APP, MSG> Program<APP, MSG>
where
MSG: 'static,
APP: Application<MSG> + 'static,
APP: Application<MSG>,
{
/// create a text node
pub fn create_text_node(txt: &str) -> Text {
Expand Down
Loading

0 comments on commit f30f4a4

Please sign in to comment.