Changeset 104210 in webkit
- Timestamp:
- Jan 5, 2012 1:48:50 PM (12 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r104208 r104210 1 2012-01-05 Ryosuke Niwa <rniwa@webkit.org> 2 3 Inserting nodes is slow due to Node::notifyNodeListsAttributeChanged (20%+) 4 https://bugs.webkit.org/show_bug.cgi?id=73853 5 6 Reviewed by Antti Koivisto. 7 8 Lazily invalidate the node list caches instead of invaliding them at the time of modification. We use 9 the DOM tree version to detect whether caches need to be invalidated or not. We now invalidate caches more 10 frequently after this patch (in particular, invalidates caches that are stored on nodes not present in 11 the ancestry of the modified nodes); however, our study on major Web sites such as Gmail, Facebook, Twitter, 12 etc... indicate that about 1% of real-world usage benefits from keeping the caches alive across different 13 DOM tree versions. 14 15 In order to invalidate caches lazily, this patch adds replaces the type of m_caches in DynamicSubtreeNodeList 16 by DynamicSubtreeNodeList::SubtreeCaches which encapsulates member variables in DynamicNodeList::Caches and 17 invalidates values as needed. Also this change allows m_caches to be allocated as a part of 18 DynamicSubtreeNodeList instead of a separate ref-counted object. 19 20 * dom/Attr.cpp: 21 (WebCore::Attr::setValue): 22 (WebCore::Attr::childrenChanged): 23 * dom/DynamicNodeList.cpp: 24 (WebCore::DynamicSubtreeNodeList::DynamicSubtreeNodeList): 25 (WebCore::DynamicSubtreeNodeList::length): 26 (WebCore::DynamicSubtreeNodeList::itemForwardsFromCurrent): 27 (WebCore::DynamicSubtreeNodeList::itemBackwardsFromCurrent): 28 (WebCore::DynamicSubtreeNodeList::item): 29 (WebCore::DynamicSubtreeNodeList::invalidateCache): 30 (WebCore::DynamicNodeList::Caches::create): 31 (WebCore::DynamicNodeList::Caches::reset): 32 * dom/DynamicNodeList.h: 33 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches): Added. 34 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::isLengthCacheValid): Added. 35 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::isItemCacheValid): Added. 36 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedLength): Added. 37 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItem): Added. 38 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItemOffset): Added. 39 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::setLengthCache): Added. 40 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::setItemCache): Added. 41 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::reset): Added. 42 (WebCore::DynamicSubtreeNodeList::SubtreeCaches::domVersionIsConsistent): Added. 43 * dom/Element.cpp: 44 (WebCore::Element::updateAfterAttributeChanged): 45 * dom/Node.cpp: 46 (WebCore::Node::setTreeScopeRecursively): Clear caches when a node moves from one document to another. 47 (WebCore::Node::invalidateNodeListsCacheAfterAttributeChanged): Only clears child node list of Attr. 48 (WebCore::Node::invalidateNodeListsCacheAfterChildrenChanged): Only clears child node list. 49 (WebCore::NodeListsNodeData::invalidateCaches): Merged with invalidateCachesThatDependOnAttributes. 50 * dom/Node.h: 51 * dom/NodeRareData.h: 52 * html/HTMLElement.cpp: 53 (WebCore::HTMLElement::parseMappedAttribute): 54 * html/HTMLLabelElement.cpp: 55 * html/HTMLLabelElement.h: 56 1 57 2012-01-05 Ojan Vafai <ojan@chromium.org> 2 58 -
trunk/Source/WebCore/dom/Attr.cpp
r104116 r104210 130 130 m_ignoreChildrenChanged--; 131 131 132 invalidateNodeListsCacheAfterAttributeChanged( m_attribute->name());132 invalidateNodeListsCacheAfterAttributeChanged(); 133 133 } 134 134 … … 175 175 Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 176 176 177 invalidateNodeListsCacheAfterAttributeChanged( m_attribute->name());177 invalidateNodeListsCacheAfterAttributeChanged(); 178 178 179 179 // FIXME: We should include entity references in the value -
trunk/Source/WebCore/dom/DynamicNodeList.cpp
r104116 r104210 29 29 namespace WebCore { 30 30 31 DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches() 32 : m_cachedItem(0) 33 , m_isLengthCacheValid(false) 34 , m_isItemCacheValid(false) 35 , m_domTreeVersionAtTimeOfCaching(0) 36 { 37 } 38 39 void DynamicSubtreeNodeList::SubtreeCaches::setLengthCache(Node* node, unsigned length) 40 { 41 if (m_isItemCacheValid && !domVersionIsConsistent()) { 42 m_cachedItem = node; 43 m_isItemCacheValid = false; 44 } 45 m_cachedLength = length; 46 m_isLengthCacheValid = true; 47 m_domTreeVersionAtTimeOfCaching = node->document()->domTreeVersion(); 48 } 49 50 void DynamicSubtreeNodeList::SubtreeCaches::setItemCache(Node* item, unsigned offset) 51 { 52 if (m_isLengthCacheValid && !domVersionIsConsistent()) 53 m_isLengthCacheValid = false; 54 m_cachedItem = item; 55 m_cachedItemOffset = offset; 56 m_isItemCacheValid = true; 57 m_domTreeVersionAtTimeOfCaching = item->document()->domTreeVersion(); 58 } 59 60 void DynamicSubtreeNodeList::SubtreeCaches::reset() 61 { 62 m_cachedItem = 0; 63 m_isLengthCacheValid = false; 64 m_isItemCacheValid = false; 65 } 66 31 67 DynamicSubtreeNodeList::DynamicSubtreeNodeList(PassRefPtr<Node> node) 32 68 : DynamicNodeList(node) 33 , m_caches(Caches::create())34 69 { 35 70 rootNode()->registerDynamicSubtreeNodeList(this); … … 43 78 unsigned DynamicSubtreeNodeList::length() const 44 79 { 45 if (m_caches ->isLengthCacheValid)46 return m_caches ->cachedLength;80 if (m_caches.isLengthCacheValid()) 81 return m_caches.cachedLength(); 47 82 48 83 unsigned length = 0; … … 51 86 length += n->isElementNode() && nodeMatches(static_cast<Element*>(n)); 52 87 53 m_caches->cachedLength = length; 54 m_caches->isLengthCacheValid = true; 88 m_caches.setLengthCache(node(), length); 55 89 56 90 return length; … … 63 97 if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) { 64 98 if (!remainingOffset) { 65 m_caches->lastItem = n; 66 m_caches->lastItemOffset = offset; 67 m_caches->isItemCacheValid = true; 99 m_caches.setItemCache(n, offset); 68 100 return n; 69 101 } … … 81 113 if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) { 82 114 if (!remainingOffset) { 83 m_caches->lastItem = n; 84 m_caches->lastItemOffset = offset; 85 m_caches->isItemCacheValid = true; 115 m_caches.setItemCache(n, offset); 86 116 return n; 87 117 } … … 97 127 int remainingOffset = offset; 98 128 Node* start = node()->firstChild(); 99 if (m_caches ->isItemCacheValid) {100 if (offset == m_caches ->lastItemOffset)101 return m_caches ->lastItem;102 else if (offset > m_caches->lastItemOffset || m_caches->lastItemOffset- offset < offset) {103 start = m_caches ->lastItem;104 remainingOffset -= m_caches ->lastItemOffset;129 if (m_caches.isItemCacheValid()) { 130 if (offset == m_caches.cachedItemOffset()) 131 return m_caches.cachedItem(); 132 if (offset > m_caches.cachedItemOffset() || m_caches.cachedItemOffset() - offset < offset) { 133 start = m_caches.cachedItem(); 134 remainingOffset -= m_caches.cachedItemOffset(); 105 135 } 106 136 } … … 140 170 void DynamicSubtreeNodeList::invalidateCache() 141 171 { 142 m_caches ->reset();172 m_caches.reset(); 143 173 } 144 174 … … 150 180 } 151 181 152 PassRefPtr<Dynamic SubtreeNodeList::Caches> DynamicSubtreeNodeList::Caches::create()182 PassRefPtr<DynamicNodeList::Caches> DynamicNodeList::Caches::create() 153 183 { 154 184 return adoptRef(new Caches()); 155 185 } 156 186 157 void Dynamic SubtreeNodeList::Caches::reset()187 void DynamicNodeList::Caches::reset() 158 188 { 159 189 lastItem = 0; -
trunk/Source/WebCore/dom/DynamicNodeList.h
r104116 r104210 25 25 #define DynamicNodeList_h 26 26 27 #include "Document.h" 27 28 #include "NodeList.h" 28 29 #include <wtf/RefCounted.h> … … 46 47 bool isLengthCacheValid : 1; 47 48 bool isItemCacheValid : 1; 49 48 50 protected: 49 51 Caches(); 50 52 }; 53 51 54 DynamicNodeList(PassRefPtr<Node> node) 52 55 : m_node(node) … … 77 80 protected: 78 81 DynamicSubtreeNodeList(PassRefPtr<Node> rootNode); 79 mutable RefPtr<Caches> m_caches;80 82 81 83 private: 84 85 class SubtreeCaches { 86 public: 87 SubtreeCaches(); 88 89 bool isLengthCacheValid() const { return m_isLengthCacheValid && domVersionIsConsistent(); } 90 bool isItemCacheValid() const { return m_isItemCacheValid && domVersionIsConsistent(); } 91 92 unsigned cachedLength() const { return m_cachedLength; } 93 Node* cachedItem() const { return m_cachedItem; } 94 unsigned cachedItemOffset() const { return m_cachedItemOffset; } 95 96 void setLengthCache(Node* nodeForDocumentVersion, unsigned length); 97 void setItemCache(Node*, unsigned offset); 98 void reset(); 99 100 private: 101 Node* m_cachedItem; 102 unsigned m_cachedItemOffset; 103 unsigned m_cachedLength; 104 bool m_isLengthCacheValid : 1; 105 bool m_isItemCacheValid : 1; 106 107 bool domVersionIsConsistent() const 108 { 109 return m_cachedItem && m_cachedItem->document()->domTreeVersion() == m_domTreeVersionAtTimeOfCaching; 110 } 111 uint64_t m_domTreeVersionAtTimeOfCaching; 112 }; 113 114 mutable SubtreeCaches m_caches; 115 82 116 virtual bool isDynamicNodeList() const; 83 117 Node* itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const; -
trunk/Source/WebCore/dom/Element.cpp
r104165 r104210 675 675 void Element::updateAfterAttributeChanged(Attribute* attr) 676 676 { 677 invalidateNodeListsCacheAfterAttributeChanged( attr->name());677 invalidateNodeListsCacheAfterAttributeChanged(); 678 678 679 679 if (!AXObjectCache::accessibilityEnabled()) -
trunk/Source/WebCore/dom/Node.cpp
r104116 r104210 490 490 491 491 if (node->hasRareData() && node->rareData()->nodeLists()) { 492 node->rareData()->nodeLists()->invalidateCaches(); 492 493 if (currentTreeScope) 493 494 currentTreeScope->removeNodeListCache(); … … 1003 1004 } 1004 1005 1005 void Node::invalidateNodeListsCacheAfterAttributeChanged( const QualifiedName& attrName)1006 void Node::invalidateNodeListsCacheAfterAttributeChanged() 1006 1007 { 1007 1008 if (hasRareData() && isAttributeNode()) { … … 1010 1011 data->clearChildNodeListCache(); 1011 1012 } 1012 1013 // This list should be sync'ed with NodeListsNodeData.1014 if (attrName != classAttr1015 #if ENABLE(MICRODATA)1016 && attrName != itemscopeAttr1017 && attrName != itempropAttr1018 #endif1019 && attrName != nameAttr)1020 return;1021 1022 if (!treeScope()->hasNodeListCaches())1023 return;1024 1025 for (Node* node = this; node; node = node->parentNode()) {1026 ASSERT(this == node || !node->isAttributeNode());1027 if (!node->hasRareData())1028 continue;1029 NodeRareData* data = node->rareData();1030 if (!data->nodeLists())1031 continue;1032 1033 data->nodeLists()->invalidateCachesThatDependOnAttributes();1034 removeNodeListCacheIfPossible(node, data);1035 }1036 1013 } 1037 1014 … … 1040 1017 if (hasRareData()) 1041 1018 rareData()->clearChildNodeListCache(); 1042 1043 if (!treeScope()->hasNodeListCaches())1044 return;1045 for (Node* node = this; node; node = node->parentNode()) {1046 if (!node->hasRareData())1047 continue;1048 NodeRareData* data = node->rareData();1049 if (!data->nodeLists())1050 continue;1051 1052 data->nodeLists()->invalidateCaches();1053 1054 NodeListsNodeData::NodeListSet::iterator end = data->nodeLists()->m_listsWithCaches.end();1055 for (NodeListsNodeData::NodeListSet::iterator it = data->nodeLists()->m_listsWithCaches.begin(); it != end; ++it)1056 (*it)->invalidateCache();1057 1058 removeNodeListCacheIfPossible(node, data);1059 }1060 }1061 1062 void Node::notifyLocalNodeListsLabelChanged()1063 {1064 if (!hasRareData())1065 return;1066 NodeRareData* data = rareData();1067 if (!data->nodeLists())1068 return;1069 1070 if (data->nodeLists()->m_labelsNodeListCache)1071 data->nodeLists()->m_labelsNodeListCache->invalidateCache();1072 1019 } 1073 1020 … … 2244 2191 } 2245 2192 2246 #if ENABLE(MICRODATA)2247 void Node::itemTypeAttributeChanged()2248 {2249 Node * rootNode = document();2250 2251 if (!rootNode->hasRareData())2252 return;2253 2254 NodeRareData* data = rootNode->rareData();2255 2256 if (!data->nodeLists())2257 return;2258 2259 data->nodeLists()->invalidateMicrodataItemListCaches();2260 }2261 #endif2262 2263 2193 #ifndef NDEBUG 2264 2194 … … 2389 2319 for (TagNodeListCacheNS::const_iterator it = m_tagNodeListCacheNS.begin(); it != tagCacheNSEnd; ++it) 2390 2320 it->second->invalidateCache(); 2391 invalidateCachesThatDependOnAttributes(); 2392 } 2393 2394 void NodeListsNodeData::invalidateCachesThatDependOnAttributes() 2395 { 2321 2396 2322 ClassNodeListCache::iterator classCacheEnd = m_classNodeListCache.end(); 2397 2323 for (ClassNodeListCache::iterator it = m_classNodeListCache.begin(); it != classCacheEnd; ++it) … … 2407 2333 invalidateMicrodataItemListCaches(); 2408 2334 #endif 2335 2409 2336 } 2410 2337 -
trunk/Source/WebCore/dom/Node.h
r104116 r104210 522 522 void registerDynamicSubtreeNodeList(DynamicSubtreeNodeList*); 523 523 void unregisterDynamicSubtreeNodeList(DynamicSubtreeNodeList*); 524 void invalidateNodeListsCacheAfterAttributeChanged( const QualifiedName&);524 void invalidateNodeListsCacheAfterAttributeChanged(); 525 525 void invalidateNodeListsCacheAfterChildrenChanged(); 526 void notifyLocalNodeListsLabelChanged();527 526 void removeCachedClassNodeList(ClassNodeList*, const String&); 528 527 … … 592 591 593 592 #if ENABLE(MICRODATA) 594 void itemTypeAttributeChanged();595 596 593 DOMSettableTokenList* itemProp(); 597 594 DOMSettableTokenList* itemRef(); -
trunk/Source/WebCore/dom/NodeRareData.h
r104116 r104210 79 79 80 80 void invalidateCaches(); 81 void invalidateCachesThatDependOnAttributes();82 81 83 82 #if ENABLE(MICRODATA) -
trunk/Source/WebCore/html/HTMLElement.cpp
r104116 r104210 238 238 } else if (attr->name() == itemtypeAttr) { 239 239 setItemType(attr->value()); 240 itemTypeAttributeChanged();241 240 #endif 242 241 } -
trunk/Source/WebCore/html/HTMLLabelElement.cpp
r104116 r104210 155 155 } 156 156 157 void HTMLLabelElement::parseMappedAttribute(Attribute* attribute)158 {159 if (attribute->name() == forAttr) {160 // htmlFor attribute change affects other nodes than this.161 // Clear the caches to ensure that the labels caches are cleared.162 if (document())163 document()->notifyLocalNodeListsLabelChanged();164 } else165 HTMLElement::parseMappedAttribute(attribute);166 }167 168 157 } // namespace -
trunk/Source/WebCore/html/HTMLLabelElement.h
r104116 r104210 51 51 52 52 void focus(bool restorePreviousSelection = true); 53 54 virtual void parseMappedAttribute(Attribute*);55 53 }; 56 54
Note: See TracChangeset
for help on using the changeset viewer.