* [yew-devel] [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo
@ 2025-09-29 13:12 Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 2/3] state: keyed slab tree: add key -> node_id cache Dominik Csapak
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Dominik Csapak @ 2025-09-29 13:12 UTC (permalink / raw)
To: yew-devel
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/state/tree_store/keyed_slab_tree.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/state/tree_store/keyed_slab_tree.rs b/src/state/tree_store/keyed_slab_tree.rs
index b910bec..e26780c 100644
--- a/src/state/tree_store/keyed_slab_tree.rs
+++ b/src/state/tree_store/keyed_slab_tree.rs
@@ -90,7 +90,7 @@ impl<T> KeyedSlabTreeNodeMut<'_, T> {
self.visit_children(visitor);
}
- /// Retunrs the unique record key.
+ /// Returns the unique record key.
pub fn key(&self) -> Key {
self.tree.extract_key(self.record())
}
--
2.47.3
_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* [yew-devel] [PATCH yew-widget-toolkit 2/3] state: keyed slab tree: add key -> node_id cache
2025-09-29 13:12 [yew-devel] [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dominik Csapak
@ 2025-09-29 13:12 ` Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 3/3] state: keyed slab tree: panic on inserting duplicate keys in debug mode Dominik Csapak
2025-10-03 6:22 ` [yew-devel] applied: [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dietmar Maurer
2 siblings, 0 replies; 4+ messages in thread
From: Dominik Csapak @ 2025-09-29 13:12 UTC (permalink / raw)
To: yew-devel
The methods `find_node_by_key` and `find_node_by_key_mut` have to
linearly search for the key in the tree. When doing that for a large
number of keys, this becomes very slow.
To speed that up, introduce a `key_cache`, which is just a
`HashMap<Key, uszie>` that points to the node_id in the slab tree.
We have to be careful to update this cache with every insert or remove
operation.
* on insert, add the new entry into the cache
* on removal, remove the entry and do it recursively for all children of
that node
* on bulk append: iterate over the newly added subtree and add the keys
* on tree/root replace: recalculate the cache for the whole tree.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/state/tree_store/keyed_slab_tree.rs | 94 ++++++++++++++++++-------
1 file changed, 69 insertions(+), 25 deletions(-)
diff --git a/src/state/tree_store/keyed_slab_tree.rs b/src/state/tree_store/keyed_slab_tree.rs
index e26780c..df163a2 100644
--- a/src/state/tree_store/keyed_slab_tree.rs
+++ b/src/state/tree_store/keyed_slab_tree.rs
@@ -1,5 +1,5 @@
-use std::cmp::Ordering;
use std::collections::HashSet;
+use std::{cmp::Ordering, collections::HashMap};
use slab::Slab;
@@ -30,6 +30,8 @@ pub struct KeyedSlabTree<T> {
listeners: Slab<Callback<()>>,
+ key_cache: HashMap<Key, usize>,
+
pub(crate) view_root: bool,
}
@@ -98,7 +100,7 @@ impl<T> KeyedSlabTreeNodeMut<'_, T> {
/// Find a node by key.
pub fn find_node_by_key(&self, key: &Key) -> Option<KeyedSlabTreeNodeRef<T>> {
self.tree
- .find_subnode_by_key(self.node_id, key)
+ .find_subnode_by_key(key)
.map(|node_id| KeyedSlabTreeNodeRef {
node_id,
tree: self.tree,
@@ -108,7 +110,7 @@ impl<T> KeyedSlabTreeNodeMut<'_, T> {
/// Find a node by key (mutable).
pub fn find_node_by_key_mut(&mut self, key: &Key) -> Option<KeyedSlabTreeNodeMut<T>> {
self.tree
- .find_subnode_by_key(self.node_id, key)
+ .find_subnode_by_key(key)
.map(|node_id| KeyedSlabTreeNodeMut {
node_id,
tree: self.tree,
@@ -189,7 +191,7 @@ impl<T> KeyedSlabTreeNodeRef<'_, T> {
/// Find a node by key.
pub fn find_node_by_key(&self, key: &Key) -> Option<KeyedSlabTreeNodeRef<T>> {
self.tree
- .find_subnode_by_key(self.node_id, key)
+ .find_subnode_by_key(key)
.map(|node_id| KeyedSlabTreeNodeRef {
node_id,
tree: self.tree,
@@ -240,6 +242,7 @@ impl<T> KeyedSlabTree<T> {
filter: None,
listeners: Slab::new(),
view_root: true,
+ key_cache: HashMap::new(),
}
}
@@ -374,7 +377,10 @@ impl<T> KeyedSlabTree<T> {
///
/// The current tree (if any) is discarded.
pub fn set_root(&mut self, record: T) -> KeyedSlabTreeNodeMut<T> {
+ self.key_cache.clear();
+ let key = self.extract_key(&record);
let node = self.tree.set_root(record);
+ self.key_cache.insert(key, node.node_id);
KeyedSlabTreeNodeMut {
node_id: node.node_id,
tree: self,
@@ -386,6 +392,7 @@ impl<T> KeyedSlabTree<T> {
/// The current tree (if any) is discarded.
pub fn set_root_tree(&mut self, data: impl Into<SlabTree<T>>) {
self.tree.set_root_tree(data);
+ self.key_cache = self.recalculate_cache();
}
/// Set the whole tree, but safe/restore expanded state
@@ -396,7 +403,7 @@ impl<T> KeyedSlabTree<T> {
Some(root) => root.extract_expanded_state(),
None => HashSet::new(),
};
- self.tree.set_root_tree(data);
+ self.set_root_tree(data);
if let Some(mut root) = self.root_mut() {
root.apply_expanded_state(&expanded_state);
}
@@ -437,15 +444,62 @@ impl<T> KeyedSlabTree<T> {
level: usize,
parent: usize,
) -> usize {
- self.tree
- .append_subtree_node(subtree, subtree_node, level, parent)
+ let node_id = self
+ .tree
+ .append_subtree_node(subtree, subtree_node, level, parent);
+
+ let key_cache = self.calculate_cache(node_id);
+ self.key_cache.extend(key_cache);
+
+ node_id
+ }
+
+ fn remove_subtree_from_cache(&mut self, node_id: usize) {
+ if let Some(entry) = self.tree.get(node_id) {
+ self.key_cache.remove(&self.extract_key(&entry.record));
+ if let Some(children) = entry.children.clone() {
+ for child in children {
+ self.remove_subtree_from_cache(child);
+ }
+ }
+ }
}
fn remove_node_id(&mut self, node_id: usize) -> Option<T> {
+ self.remove_subtree_from_cache(node_id);
self.tree.remove_node_id(node_id)
}
+ fn calculate_cache(&self, node_id: usize) -> HashMap<Key, usize> {
+ let mut map = HashMap::new();
+ if let Some(node) = self.get(node_id) {
+ let key = self.extract_key(&node.record);
+ map.insert(key, node_id);
+ match node.children.clone() {
+ Some(list) => {
+ for child_id in list {
+ map.extend(self.calculate_cache(child_id));
+ }
+ }
+ None => {}
+ }
+ }
+
+ map
+ }
+
+ fn recalculate_cache(&self) -> HashMap<Key, usize> {
+ match self.tree.root_id {
+ Some(root_id) => self.calculate_cache(root_id),
+ None => HashMap::new(),
+ }
+ }
+
fn remove_tree_node_id(&mut self, node_id: usize) -> Option<KeyedSlabTree<T>> {
+ let key_cache = self.calculate_cache(node_id);
+ for key in key_cache.keys() {
+ self.key_cache.remove(key);
+ }
match self.tree.remove_tree_node_id(node_id) {
Some(tree) => Some(KeyedSlabTree {
extract_key: self.extract_key.clone(),
@@ -456,37 +510,27 @@ impl<T> KeyedSlabTree<T> {
filter: None,
listeners: Slab::new(),
view_root: true,
+ key_cache,
}),
None => None,
}
}
fn insert_record(&mut self, record: T, parent_id: Option<usize>) -> usize {
- self.tree.insert_record(record, parent_id)
+ let key = self.extract_key(&record);
+ let node_id = self.tree.insert_record(record, parent_id);
+ self.key_cache.insert(key, node_id);
+ node_id
}
- fn find_subnode_by_key(&self, node_id: usize, key: &Key) -> Option<usize> {
- let entry = self.get(node_id)?;
-
- if key == &self.extract_key(&entry.record) {
- return Some(node_id);
- }
-
- if let Some(children) = &entry.children {
- for child_id in children {
- if let Some(pos) = self.find_subnode_by_key(*child_id, key) {
- return Some(pos);
- }
- }
- }
-
- None
+ fn find_subnode_by_key(&self, key: &Key) -> Option<usize> {
+ self.key_cache.get(key).copied()
}
fn find_node_by_key(&self, key: &Key) -> Option<usize> {
match self.tree.root_id {
None => None,
- Some(root_id) => self.find_subnode_by_key(root_id, key),
+ Some(_root_id) => self.find_subnode_by_key(key),
}
}
--
2.47.3
_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* [yew-devel] [PATCH yew-widget-toolkit 3/3] state: keyed slab tree: panic on inserting duplicate keys in debug mode
2025-09-29 13:12 [yew-devel] [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 2/3] state: keyed slab tree: add key -> node_id cache Dominik Csapak
@ 2025-09-29 13:12 ` Dominik Csapak
2025-10-03 6:22 ` [yew-devel] applied: [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dietmar Maurer
2 siblings, 0 replies; 4+ messages in thread
From: Dominik Csapak @ 2025-09-29 13:12 UTC (permalink / raw)
To: yew-devel
check on insert/append if the keys already exist, but only in debug mode
to not unnecessarily slow down release builds.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/state/tree_store/keyed_slab_tree.rs | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/state/tree_store/keyed_slab_tree.rs b/src/state/tree_store/keyed_slab_tree.rs
index df163a2..c7044d1 100644
--- a/src/state/tree_store/keyed_slab_tree.rs
+++ b/src/state/tree_store/keyed_slab_tree.rs
@@ -449,6 +449,12 @@ impl<T> KeyedSlabTree<T> {
.append_subtree_node(subtree, subtree_node, level, parent);
let key_cache = self.calculate_cache(node_id);
+ #[cfg(debug_assertions)]
+ for key in key_cache.keys() {
+ if self.key_cache.contains_key(key) {
+ panic!("append_subtree_node: duplicate key {key:?}");
+ }
+ }
self.key_cache.extend(key_cache);
node_id
@@ -519,6 +525,10 @@ impl<T> KeyedSlabTree<T> {
fn insert_record(&mut self, record: T, parent_id: Option<usize>) -> usize {
let key = self.extract_key(&record);
let node_id = self.tree.insert_record(record, parent_id);
+ #[cfg(debug_assertions)]
+ if self.key_cache.contains_key(&key) {
+ panic!("insert_record: duplicate key {key:?}");
+ }
self.key_cache.insert(key, node_id);
node_id
}
--
2.47.3
_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
* [yew-devel] applied: [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo
2025-09-29 13:12 [yew-devel] [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 2/3] state: keyed slab tree: add key -> node_id cache Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 3/3] state: keyed slab tree: panic on inserting duplicate keys in debug mode Dominik Csapak
@ 2025-10-03 6:22 ` Dietmar Maurer
2 siblings, 0 replies; 4+ messages in thread
From: Dietmar Maurer @ 2025-10-03 6:22 UTC (permalink / raw)
To: Yew framework devel list at Proxmox
[-- Attachment #1.1: Type: text/plain, Size: 21 bytes --]
applied all 3 patches
[-- Attachment #1.2: Type: text/html, Size: 333 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-10-03 6:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-29 13:12 [yew-devel] [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 2/3] state: keyed slab tree: add key -> node_id cache Dominik Csapak
2025-09-29 13:12 ` [yew-devel] [PATCH yew-widget-toolkit 3/3] state: keyed slab tree: panic on inserting duplicate keys in debug mode Dominik Csapak
2025-10-03 6:22 ` [yew-devel] applied: [PATCH yew-widget-toolkit 1/3] state: keyed slab tree: fix typo Dietmar Maurer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox