From 2ccd9d47122732b31fb61259efdcd4fb9180014b Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Tue, 17 Sep 2024 20:49:15 +0200 Subject: [PATCH] xilem_web: Optimize element casting, and intern often used strings (e.g. "div") (#594) Some micro-optimizations, e.g. avoids a js call `instanceof` (via `dyn_into`/`dyn_ref`) when constructing elements. Leads to a small speed increase when constructing/visiting elements (roughly 2%) and more importantly a leaner wasm binary (around 5 %), respectively tested with the js-framework-benchmark suite. --- xilem_web/Cargo.toml | 2 ++ xilem_web/src/attribute.rs | 8 ++------ xilem_web/src/class.rs | 2 +- xilem_web/src/element_props.rs | 7 +++++-- xilem_web/src/elements.rs | 2 +- xilem_web/src/events.rs | 4 ++-- xilem_web/src/lib.rs | 2 +- xilem_web/src/pointer.rs | 2 +- xilem_web/src/style.rs | 6 ++---- xilem_web/src/text.rs | 4 ++-- 10 files changed, 19 insertions(+), 20 deletions(-) diff --git a/xilem_web/Cargo.toml b/xilem_web/Cargo.toml index e98b94f58..063461082 100644 --- a/xilem_web/Cargo.toml +++ b/xilem_web/Cargo.toml @@ -179,3 +179,5 @@ features = [ [features] default = ["hydration"] hydration = [] +# This interns some often used strings, such as element tags ("div" etc.), which slightly improves performance when creating elements at the cost of a bigger wasm binary +intern_strings = ["wasm-bindgen/enable-interning"] diff --git a/xilem_web/src/attribute.rs b/xilem_web/src/attribute.rs index 7f248c771..04c83375b 100644 --- a/xilem_web/src/attribute.rs +++ b/xilem_web/src/attribute.rs @@ -127,17 +127,13 @@ impl Attributes { for modifier in self.attribute_modifiers.iter().rev() { match modifier { AttributeModifier::Remove(name) => { - if self.updated_attributes.contains_key(name) { - self.updated_attributes.remove(name); + if self.updated_attributes.remove(name).is_some() { remove_attribute(element, name); - // element.remove_attribute(name); } } AttributeModifier::Set(name, value) => { - if self.updated_attributes.contains_key(name) { - self.updated_attributes.remove(name); + if self.updated_attributes.remove(name).is_some() { set_attribute(element, name, &value.serialize()); - // element.set_attribute(name, &value.serialize()); } } AttributeModifier::EndMarker(_) => (), diff --git a/xilem_web/src/class.rs b/xilem_web/src/class.rs index 7c3a75267..b585698a7 100644 --- a/xilem_web/src/class.rs +++ b/xilem_web/src/class.rs @@ -149,7 +149,7 @@ impl Classes { // Svg elements do have issues with className, see https://developer.mozilla.org/en-US/docs/Web/API/Element/className if element.dyn_ref::().is_some() { element - .set_attribute("class", &self.class_name) + .set_attribute(wasm_bindgen::intern("class"), &self.class_name) .unwrap_throw(); } else { element.set_class_name(&self.class_name); diff --git a/xilem_web/src/element_props.rs b/xilem_web/src/element_props.rs index e59d6972c..3567c73c1 100644 --- a/xilem_web/src/element_props.rs +++ b/xilem_web/src/element_props.rs @@ -70,7 +70,10 @@ impl Pod { /// Creates a new Pod with [`web_sys::Element`] as element and `ElementProps` as its [`DomView::Props`](`crate::DomView::Props`) pub fn new_element(children: Vec, ns: &str, elem_name: &str) -> Self { let element = document() - .create_element_ns(Some(ns), elem_name) + .create_element_ns( + Some(wasm_bindgen::intern(ns)), + wasm_bindgen::intern(elem_name), + ) .unwrap_throw(); for child in children.iter() { @@ -93,7 +96,7 @@ impl Pod { #[cfg(feature = "hydration")] pub fn hydrate_element(children: Vec, element: web_sys::Node) -> Self { Self { - node: element.dyn_into().unwrap_throw(), + node: element.unchecked_into(), props: ElementProps { in_hydration: true, attributes: None, diff --git a/xilem_web/src/elements.rs b/xilem_web/src/elements.rs index d580342b8..513189edc 100644 --- a/xilem_web/src/elements.rs +++ b/xilem_web/src/elements.rs @@ -381,7 +381,7 @@ where .replace_child(&new_element, element.node) .unwrap_throw(); } - *element.node = new_element.dyn_into().unwrap_throw(); + *element.node = new_element.unchecked_into(); } rebuild_element( diff --git a/xilem_web/src/events.rs b/xilem_web/src/events.rs index 0f0f1525c..7e3af842a 100644 --- a/xilem_web/src/events.rs +++ b/xilem_web/src/events.rs @@ -74,7 +74,7 @@ fn create_event_listener( ) -> Closure { let thunk = ctx.message_thunk(); let callback = Closure::new(move |event: web_sys::Event| { - let event = event.dyn_into::().unwrap_throw(); + let event = event.unchecked_into::(); thunk.push_message(event); }); @@ -545,7 +545,7 @@ where ctx.with_id(ON_EVENT_VIEW_ID, |ctx| { let thunk = ctx.message_thunk(); let callback = Closure::new(move |entries: js_sys::Array| { - let entry: web_sys::ResizeObserverEntry = entries.at(0).dyn_into().unwrap_throw(); + let entry: web_sys::ResizeObserverEntry = entries.at(0).unchecked_into(); thunk.push_message(entry); }); diff --git a/xilem_web/src/lib.rs b/xilem_web/src/lib.rs index 30be8ae3e..700e9ec9b 100644 --- a/xilem_web/src/lib.rs +++ b/xilem_web/src/lib.rs @@ -409,7 +409,7 @@ macro_rules! impl_dom_node_for_elements { impl From> for Pod { fn from(value: Pod) -> Self { Self { - node: value.node.dyn_into().unwrap_throw(), + node: value.node.unchecked_into(), props: value.props, } } diff --git a/xilem_web/src/pointer.rs b/xilem_web/src/pointer.rs index c5d3de9f5..7782b1408 100644 --- a/xilem_web/src/pointer.rs +++ b/xilem_web/src/pointer.rs @@ -86,7 +86,7 @@ where ctx.with_id(ViewId::new(0), |ctx| { let (element, child_state) = self.child.build(ctx); let thunk = ctx.message_thunk(); - let el = element.as_ref().dyn_ref::().unwrap(); + let el = element.as_ref().unchecked_ref::(); let el_clone = el.clone(); let down_closure = Closure::new(move |e: PointerEvent| { thunk.push_message(PointerMsg::Down(PointerDetails::from_pointer_event(&e))); diff --git a/xilem_web/src/style.rs b/xilem_web/src/style.rs index 622814b14..5531a2799 100644 --- a/xilem_web/src/style.rs +++ b/xilem_web/src/style.rs @@ -183,14 +183,12 @@ impl Styles { for modifier in self.style_modifiers.iter().rev() { match modifier { StyleModifier::Remove(name) => { - if self.updated_styles.contains_key(name) { - self.updated_styles.remove(name); + if self.updated_styles.remove(name).is_some() { remove_style(element, name); } } StyleModifier::Set(name, value) => { - if self.updated_styles.contains_key(name) { - self.updated_styles.remove(name); + if self.updated_styles.remove(name).is_some() { set_style(element, name, value); } } diff --git a/xilem_web/src/text.rs b/xilem_web/src/text.rs index 78f50ffb2..1e98a8bf1 100644 --- a/xilem_web/src/text.rs +++ b/xilem_web/src/text.rs @@ -21,7 +21,7 @@ macro_rules! impl_string_view { ) -> (Self::OrphanElement, Self::OrphanViewState) { #[cfg(feature = "hydration")] let node = if ctx.is_hydrating() { - ctx.hydrate_node().unwrap().dyn_into().unwrap() + ctx.hydrate_node().unwrap().unchecked_into() } else { web_sys::Text::new_with_data(view).unwrap() }; @@ -81,7 +81,7 @@ macro_rules! impl_to_string_view { ) -> (Self::OrphanElement, Self::OrphanViewState) { #[cfg(feature = "hydration")] let node = if ctx.is_hydrating() { - ctx.hydrate_node().unwrap().dyn_into().unwrap() + ctx.hydrate_node().unwrap().unchecked_into() } else { web_sys::Text::new_with_data(&view.to_string()).unwrap() };