浏览代码

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
共有 2 个文件被更改,包括 26 次插入13 次删除
  1. 23 13
      Userland/Libraries/LibWeb/DOM/HTMLCollection.cpp
  2. 3 0
      Userland/Libraries/LibWeb/DOM/HTMLCollection.h

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

@@ -6,6 +6,7 @@
  */
 
 #include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
 #include <LibWeb/DOM/HTMLCollection.h>
 #include <LibWeb/DOM/ParentNode.h>
@@ -45,24 +46,33 @@ void HTMLCollection::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
     visitor.visit(m_root);
+    for (auto& element : m_cached_elements)
+        visitor.visit(element);
 }
 
 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;
 }
 

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

@@ -60,6 +60,9 @@ protected:
 private:
     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;
     Function<bool(Element const&)> m_filter;