Преглед изворни кода

LibWeb: Let HTMLCollection cache its element list

Use the new DOM tree version mechanism to allow HTMLCollection to
remember its internal list of elements instead of rebuilding it on
every access.

This avoids thousands of full DOM walks while loading our GitHub repo.

~15% speed-up on jQuery subtests in Speedometer 3.0 :^)
Andreas Kling пре 1 година
родитељ
комит
6bb4a2bfaa

+ 23 - 13
Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp

@@ -6,6 +6,7 @@
  */
  */
 
 
 #include <LibWeb/Bindings/Intrinsics.h>
 #include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/HTMLCollection.h>
 #include <LibWeb/DOM/HTMLCollection.h>
 #include <LibWeb/DOM/ParentNode.h>
 #include <LibWeb/DOM/ParentNode.h>
@@ -45,24 +46,33 @@ void HTMLCollection::visit_edges(Cell::Visitor& visitor)
 {
 {
     Base::visit_edges(visitor);
     Base::visit_edges(visitor);
     visitor.visit(m_root);
     visitor.visit(m_root);
+    for (auto& element : m_cached_elements)
+        visitor.visit(element);
 }
 }
 
 
 JS::MarkedVector<Element*> HTMLCollection::collect_matching_elements() const
 JS::MarkedVector<Element*> HTMLCollection::collect_matching_elements() const
 {
 {
-    JS::MarkedVector<Element*> elements(m_root->heap());
-    if (m_scope == Scope::Descendants) {
-        m_root->for_each_in_subtree_of_type<Element>([&](auto& element) {
-            if (m_filter(element))
-                elements.append(const_cast<Element*>(&element));
-            return IterationDecision::Continue;
-        });
-    } else {
-        m_root->for_each_child_of_type<Element>([&](auto& element) {
-            if (m_filter(element))
-                elements.append(const_cast<Element*>(&element));
-            return IterationDecision::Continue;
-        });
+    if (m_cached_dom_tree_version != root()->document().dom_tree_version()) {
+        m_cached_elements.clear();
+        if (m_scope == Scope::Descendants) {
+            m_root->for_each_in_subtree_of_type<Element>([&](auto& element) {
+                if (m_filter(element))
+                    m_cached_elements.append(element);
+                return IterationDecision::Continue;
+            });
+        } else {
+            m_root->for_each_child_of_type<Element>([&](auto& element) {
+                if (m_filter(element))
+                    m_cached_elements.append(element);
+                return IterationDecision::Continue;
+            });
+        }
+        m_cached_dom_tree_version = root()->document().dom_tree_version();
     }
     }
+
+    JS::MarkedVector<Element*> elements(heap());
+    for (auto& element : m_cached_elements)
+        elements.append(element);
     return elements;
     return elements;
 }
 }
 
 

+ 3 - 0
Userland/Libraries/LibWeb/DOM/HTMLCollection.h

@@ -60,6 +60,9 @@ protected:
 private:
 private:
     virtual void visit_edges(Cell::Visitor&) override;
     virtual void visit_edges(Cell::Visitor&) override;
 
 
+    mutable u64 m_cached_dom_tree_version { 0 };
+    mutable Vector<JS::NonnullGCPtr<Element>> m_cached_elements;
+
     JS::NonnullGCPtr<ParentNode> m_root;
     JS::NonnullGCPtr<ParentNode> m_root;
     Function<bool(Element const&)> m_filter;
     Function<bool(Element const&)> m_filter;