Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance improvements #155

Merged
merged 8 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/js_framework_bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

env:
MSG_FOOTER: |
<br/>

Workflow: [${{ github.run_id }}](/${{ github.repository }}/actions/runs/${{ github.run_id }})
*Adding new commits will generate a new report*

Expand Down
30 changes: 28 additions & 2 deletions packages/sycamore-macro/src/template/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use quote::{quote, quote_spanned, ToTokens};
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
use syn::{token, Ident, Token};
use syn::{token, Expr, Ident, Token};

use super::*;

Expand Down Expand Up @@ -62,8 +62,23 @@ impl ToTokens for Element {
let multi = children.body.len() != 1;
let mut children = children.body.iter().peekable();
while let Some(child) = children.next() {
// True if the child is a component or a splice that is not a simple path.
// Example:
// template! { MyComponent() } // is_dynamic = true
// template! { (state.get()) } // is_dynamic = true
// template! { (state) } // is_dynamic = false
let is_dynamic = match child {
HtmlTree::Component(_) => true,
HtmlTree::Text(Text::Splice(_, expr))
if !matches!(expr.as_ref(), Expr::Path(_)) =>
{
true
}
_ => false,
};

quoted.extend(match child {
HtmlTree::Component(_) | HtmlTree::Text(Text::Splice(..)) => {
_ if is_dynamic => {
let quote_marker =
if let Some(HtmlTree::Element(element)) =
children.next_if(|x| matches!(x, HtmlTree::Element(_)))
Expand Down Expand Up @@ -123,6 +138,17 @@ impl ToTokens for Element {
&::sycamore::generic_node::GenericNode::text_node(#text),
);
},
HtmlTree::Text(text @ Text::Splice(..)) => {
assert!(!is_dynamic);
quote_spanned! { text.span()=>
::sycamore::utils::render::insert(
&__el,
::sycamore::template::IntoTemplate::create(&#text),
None, None, #multi
);
}
},
_ => unreachable!("all branches covered by match guards"),
});
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/sycamore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ html-escape = {version = "0.2.7", optional = true}
indexmap = "1.7"
js-sys = {version = "0.3", optional = true}
serde = {version = "1.0", optional = true}
smallvec = "1.6"
sycamore-macro = {path = "../sycamore-macro", version = "=0.5.0"}
wasm-bindgen = {version = "0.2", optional = true, features = ["enable-interning"]}

Expand Down
3 changes: 1 addition & 2 deletions packages/sycamore/src/generic_node/dom_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,7 @@ fn get_children(parent: &Element) -> Vec<Element> {
let children = parent.children();
let children_count = children.length();

let mut vec = Vec::new();
vec.reserve(children_count as usize);
let mut vec = Vec::with_capacity(children_count as usize);

for i in 0..children.length() {
vec.push(children.get_with_index(i).unwrap());
Expand Down
19 changes: 11 additions & 8 deletions packages/sycamore/src/rx/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ use std::ptr;
use std::rc::Rc;
use std::rc::Weak;

use smallvec::SmallVec;

use super::*;

/// The number of effects that are allocated on the stack before resorting to heap allocation in
/// [`ReactiveScope`].
const REACTIVE_SCOPE_EFFECTS_STACK_CAPACITY: usize = 4;

/// Initial capacity for [`CONTEXTS`].
const CONTEXTS_INITIAL_CAPACITY: usize = 10;

thread_local! {
/// Context of the effect that is currently running. `None` if no effect is running.
///
/// This is an array of callbacks that, when called, will add the a `Signal` to the `handle` in the argument.
/// The callbacks return another callback which will unsubscribe the `handle` from the `Signal`.
pub(super) static CONTEXTS: RefCell<Vec<Weak<RefCell<Option<Running>>>>> = RefCell::new(Vec::new());
pub(super) static CONTEXTS: RefCell<Vec<Weak<RefCell<Option<Running>>>>> = RefCell::new(Vec::with_capacity(CONTEXTS_INITIAL_CAPACITY));
pub(super) static SCOPE: RefCell<Option<ReactiveScope>> = RefCell::new(None);
}

Expand Down Expand Up @@ -42,19 +51,13 @@ impl Running {
}
}

impl Drop for Running {
fn drop(&mut self) {
self.clear_dependencies();
}
}

/// Owns the effects created in the current reactive scope.
/// The effects are dropped and the cleanup callbacks are called when the [`ReactiveScope`] is
/// dropped.
#[derive(Default)]
pub struct ReactiveScope {
/// Effects created in this scope.
effects: Vec<Rc<RefCell<Option<Running>>>>,
effects: SmallVec<[Rc<RefCell<Option<Running>>>; REACTIVE_SCOPE_EFFECTS_STACK_CAPACITY]>,
/// Callbacks to call when the scope is dropped.
cleanup: Vec<Box<dyn FnOnce()>>,
}
Expand Down
2 changes: 1 addition & 1 deletion packages/sycamore/src/rx/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ where

// 0) Prepare a map of indices in newItems. Scan backwards so we encounter them in
// natural order.
let mut new_indices = HashMap::new();
let mut new_indices = HashMap::with_capacity(new_end - start);

// Indexes for new_indices_next are shifted by start because values at 0..start are
// always None.
Expand Down
2 changes: 1 addition & 1 deletion packages/sycamore/src/utils/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ pub fn reconcile_fragments<G: GenericNode>(parent: &G, a: &mut [G], b: &[G]) {
} else {
// Fallback to map.
if map.is_none() {
map = Some(HashMap::new());
map = Some(HashMap::with_capacity(b_end - b_start));
for (i, item) in b.iter().enumerate().take(b_end).skip(b_start) {
map.as_mut().unwrap().insert(item.clone(), i);
}
Expand Down