Title: [127438] trunk/Source/WebCore
Revision
127438
Author
[email protected]
Date
2012-09-03 19:19:26 -0700 (Mon, 03 Sep 2012)

Log Message

Share immutable ElementAttributeData between elements with identical attributes.
<http://webkit.org/b/94990>

Reviewed by Antti Koivisto.

Keep a cache of ElementAttributeData objects for a given set of attributes and reuse
them in elements with identical attribute maps. ElementAttributeData is made ref-counted
to facilitate this. A copy-on-write mechanism is already in place, since mutating call
sites have to go via Element::mutableAttributeData().

The cache is held by Document and cleared in Document::finishedParsing() since the vast
majority of immutable ElementAttributeData will be constructed during parsing.

On the HTML5 spec at <http://whatwg.org/c/>, we get a cache hit rate of nearly 80%,
translating into a 3.5MB reduction in memory use.

* dom/Document.cpp:
(WebCore::Document::finishedParsing):
(ImmutableAttributeDataCacheKey):
(WebCore::ImmutableAttributeDataCacheKey::ImmutableAttributeDataCacheKey):
(WebCore::ImmutableAttributeDataCacheKey::operator!=):
(WebCore::ImmutableAttributeDataCacheKey::hash):
(ImmutableAttributeDataCacheEntry):
(WebCore::Document::cachedImmutableAttributeData):
* dom/Document.h:
* dom/Element.cpp:
(WebCore::Element::parserSetAttributes):
* dom/Element.h:
* dom/ElementAttributeData.cpp:
(WebCore::ElementAttributeData::createImmutable):
(WebCore::ElementAttributeData::ElementAttributeData):
* dom/ElementAttributeData.h:
(WebCore::ElementAttributeData::create):
(ElementAttributeData):
(WebCore::ElementAttributeData::makeMutable):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (127437 => 127438)


--- trunk/Source/WebCore/ChangeLog	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/ChangeLog	2012-09-04 02:19:26 UTC (rev 127438)
@@ -1,3 +1,41 @@
+2012-09-03  Andreas Kling  <[email protected]>
+
+        Share immutable ElementAttributeData between elements with identical attributes.
+        <http://webkit.org/b/94990>
+
+        Reviewed by Antti Koivisto.
+
+        Keep a cache of ElementAttributeData objects for a given set of attributes and reuse
+        them in elements with identical attribute maps. ElementAttributeData is made ref-counted
+        to facilitate this. A copy-on-write mechanism is already in place, since mutating call
+        sites have to go via Element::mutableAttributeData().
+
+        The cache is held by Document and cleared in Document::finishedParsing() since the vast
+        majority of immutable ElementAttributeData will be constructed during parsing.
+
+        On the HTML5 spec at <http://whatwg.org/c/>, we get a cache hit rate of nearly 80%,
+        translating into a 3.5MB reduction in memory use.
+
+        * dom/Document.cpp:
+        (WebCore::Document::finishedParsing):
+        (ImmutableAttributeDataCacheKey):
+        (WebCore::ImmutableAttributeDataCacheKey::ImmutableAttributeDataCacheKey):
+        (WebCore::ImmutableAttributeDataCacheKey::operator!=):
+        (WebCore::ImmutableAttributeDataCacheKey::hash):
+        (ImmutableAttributeDataCacheEntry):
+        (WebCore::Document::cachedImmutableAttributeData):
+        * dom/Document.h:
+        * dom/Element.cpp:
+        (WebCore::Element::parserSetAttributes):
+        * dom/Element.h:
+        * dom/ElementAttributeData.cpp:
+        (WebCore::ElementAttributeData::createImmutable):
+        (WebCore::ElementAttributeData::ElementAttributeData):
+        * dom/ElementAttributeData.h:
+        (WebCore::ElementAttributeData::create):
+        (ElementAttributeData):
+        (WebCore::ElementAttributeData::makeMutable):
+
 2012-09-03  Peter Wang  <[email protected]>
 
         Web Inspector: the URL of worker inspector window sometimes is invalid

Modified: trunk/Source/WebCore/dom/Document.cpp (127437 => 127438)


--- trunk/Source/WebCore/dom/Document.cpp	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/Document.cpp	2012-09-04 02:19:26 UTC (rev 127438)
@@ -4886,6 +4886,10 @@
 
         InspectorInstrumentation::domContentLoadedEventFired(f.get());
     }
+
+    // The ElementAttributeData sharing cache is only used during parsing since
+    // that's when the majority of immutable attribute data will be created.
+    m_immutableAttributeDataCache.clear();
 }
 
 PassRefPtr<XPathExpression> Document::createExpression(const String& _expression_,
@@ -6172,4 +6176,73 @@
 }
 #endif
 
+class ImmutableAttributeDataCacheKey {
+public:
+    ImmutableAttributeDataCacheKey()
+        : m_localName(0)
+        , m_attributes(0)
+        , m_attributeCount(0)
+    { }
+
+    ImmutableAttributeDataCacheKey(const AtomicString& localName, const Attribute* attributes, unsigned attributeCount)
+        : m_localName(localName.impl())
+        , m_attributes(attributes)
+        , m_attributeCount(attributeCount)
+    { }
+
+    bool operator!=(const ImmutableAttributeDataCacheKey& other) const
+    {
+        if (m_localName != other.m_localName)
+            return true;
+        if (m_attributeCount != other.m_attributeCount)
+            return true;
+        return memcmp(m_attributes, other.m_attributes, sizeof(Attribute) * m_attributeCount);
+    }
+
+    unsigned hash() const
+    {
+        unsigned attributeHash = StringHasher::hashMemory(m_attributes, m_attributeCount * sizeof(Attribute));
+        return WTF::intHash((static_cast<uint64_t>(m_localName->existingHash()) << 32 | attributeHash));
+    }
+
+private:
+    AtomicStringImpl* m_localName;
+    const Attribute* m_attributes;
+    unsigned m_attributeCount;
+};
+
+struct ImmutableAttributeDataCacheEntry {
+    ImmutableAttributeDataCacheKey key;
+    RefPtr<ElementAttributeData> value;
+};
+
+PassRefPtr<ElementAttributeData> Document::cachedImmutableAttributeData(const Element* element, const Vector<Attribute>& attributes)
+{
+    ASSERT(!attributes.isEmpty());
+
+    ImmutableAttributeDataCacheKey cacheKey(element->localName(), attributes.data(), attributes.size());
+    unsigned cacheHash = cacheKey.hash();
+
+    ImmutableAttributeDataCache::iterator cacheIterator = m_immutableAttributeDataCache.add(cacheHash, nullptr).iterator;
+    if (cacheIterator->second && cacheIterator->second->key != cacheKey)
+        cacheHash = 0;
+
+    RefPtr<ElementAttributeData> attributeData;
+    if (cacheHash && cacheIterator->second)
+        attributeData = cacheIterator->second->value;
+    else
+        attributeData = ElementAttributeData::createImmutable(attributes);
+
+    if (!cacheHash || cacheIterator->second)
+        return attributeData.release();
+
+    OwnPtr<ImmutableAttributeDataCacheEntry> newEntry = adoptPtr(new ImmutableAttributeDataCacheEntry);
+    newEntry->key = ImmutableAttributeDataCacheKey(element->localName(), const_cast<const ElementAttributeData*>(attributeData.get())->attributeItem(0), attributeData->length());
+    newEntry->value = attributeData;
+
+    cacheIterator->second = newEntry.release();
+
+    return attributeData.release();
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/Document.h (127437 => 127438)


--- trunk/Source/WebCore/dom/Document.h	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/Document.h	2012-09-04 02:19:26 UTC (rev 127438)
@@ -83,6 +83,7 @@
 class DynamicNodeListCacheBase;
 class EditingText;
 class Element;
+class ElementAttributeData;
 class EntityReference;
 class Event;
 class EventListener;
@@ -206,6 +207,9 @@
 };
 const int numNodeListInvalidationTypes = InvalidateOnAnyAttrChange + 1;
 
+struct ImmutableAttributeDataCacheEntry;
+typedef HashMap<unsigned, OwnPtr<ImmutableAttributeDataCacheEntry>, AlreadyHashed> ImmutableAttributeDataCache;
+
 class Document : public ContainerNode, public TreeScope, public ScriptExecutionContext {
 public:
     static PassRefPtr<Document> create(Frame* frame, const KURL& url)
@@ -1183,6 +1187,8 @@
 
     virtual void reportMemoryUsage(MemoryObjectInfo*) const OVERRIDE;
 
+    PassRefPtr<ElementAttributeData> cachedImmutableAttributeData(const Element*, const Vector<Attribute>&);
+
 protected:
     Document(Frame*, const KURL&, bool isXHTML, bool isHTML);
 
@@ -1568,6 +1574,8 @@
     RefPtr<DOMSecurityPolicy> m_domSecurityPolicy;
 #endif
 
+    ImmutableAttributeDataCache m_immutableAttributeDataCache;
+
 #ifndef NDEBUG
     bool m_didDispatchViewportPropertiesChanged;
 #endif

Modified: trunk/Source/WebCore/dom/Element.cpp (127437 => 127438)


--- trunk/Source/WebCore/dom/Element.cpp	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/Element.cpp	2012-09-04 02:19:26 UTC (rev 127438)
@@ -823,7 +823,15 @@
         }
     }
 
-    m_attributeData = ElementAttributeData::createImmutable(filteredAttributes);
+    // When the document is in parsing state, we cache immutable ElementAttributeData objects with the
+    // input attribute vector as key. (This cache is held by Document.)
+    if (!document() || !document()->parsing())
+        m_attributeData = ElementAttributeData::createImmutable(filteredAttributes);
+    else if (!isHTMLElement()) {
+        // FIXME: Support attribute data sharing for non-HTML elements.
+        m_attributeData = ElementAttributeData::createImmutable(filteredAttributes);
+    } else
+        m_attributeData = document()->cachedImmutableAttributeData(this, filteredAttributes);
 
     // Iterate over the set of attributes we already have on the stack in case
     // attributeChanged mutates m_attributeData.

Modified: trunk/Source/WebCore/dom/Element.h (127437 => 127438)


--- trunk/Source/WebCore/dom/Element.h	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/Element.h	2012-09-04 02:19:26 UTC (rev 127438)
@@ -519,7 +519,7 @@
     ElementRareData* elementRareData() const;
     ElementRareData* ensureElementRareData();
 
-    OwnPtr<ElementAttributeData> m_attributeData;
+    RefPtr<ElementAttributeData> m_attributeData;
 };
     
 inline Element* toElement(Node* node)

Modified: trunk/Source/WebCore/dom/ElementAttributeData.cpp (127437 => 127438)


--- trunk/Source/WebCore/dom/ElementAttributeData.cpp	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/ElementAttributeData.cpp	2012-09-04 02:19:26 UTC (rev 127438)
@@ -39,10 +39,10 @@
     return sizeof(ElementAttributeData) - sizeof(void*) + sizeof(Attribute) * count;
 }
 
-PassOwnPtr<ElementAttributeData> ElementAttributeData::createImmutable(const Vector<Attribute>& attributes)
+PassRefPtr<ElementAttributeData> ElementAttributeData::createImmutable(const Vector<Attribute>& attributes)
 {
     void* slot = WTF::fastMalloc(immutableElementAttributeDataSize(attributes.size()));
-    return adoptPtr(new (slot) ElementAttributeData(attributes));
+    return adoptRef(new (slot) ElementAttributeData(attributes));
 }
 
 ElementAttributeData::ElementAttributeData()
@@ -62,17 +62,21 @@
 }
 
 ElementAttributeData::ElementAttributeData(const ElementAttributeData& other)
-    : m_inlineStyleDecl(other.m_inlineStyleDecl)
+    : RefCounted<ElementAttributeData>()
+    , m_isMutable(true)
+    , m_arraySize(0)
+    , m_inlineStyleDecl(other.m_inlineStyleDecl)
     , m_attributeStyle(other.m_attributeStyle)
     , m_classNames(other.m_classNames)
     , m_idForStyleResolution(other.m_idForStyleResolution)
-    , m_isMutable(true)
-    , m_arraySize(0)
     , m_mutableAttributeVector(new Vector<Attribute, 4>)
 {
     // This copy constructor should only be used by makeMutable() to go from immutable to mutable.
     ASSERT(!other.m_isMutable);
 
+    // An immutable ElementAttributeData should never have a mutable inline StylePropertySet attached.
+    ASSERT(!other.m_inlineStyleDecl || !other.m_inlineStyleDecl->isMutable());
+
     const Attribute* otherBuffer = reinterpret_cast<const Attribute*>(&other.m_attributes);
     for (unsigned i = 0; i < other.m_arraySize; ++i)
         m_mutableAttributeVector->append(otherBuffer[i]);

Modified: trunk/Source/WebCore/dom/ElementAttributeData.h (127437 => 127438)


--- trunk/Source/WebCore/dom/ElementAttributeData.h	2012-09-04 02:09:42 UTC (rev 127437)
+++ trunk/Source/WebCore/dom/ElementAttributeData.h	2012-09-04 02:19:26 UTC (rev 127438)
@@ -39,11 +39,11 @@
 
 enum SynchronizationOfLazyAttribute { NotInSynchronizationOfLazyAttribute, InSynchronizationOfLazyAttribute };
 
-class ElementAttributeData {
+class ElementAttributeData : public RefCounted<ElementAttributeData> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static PassOwnPtr<ElementAttributeData> create() { return adoptPtr(new ElementAttributeData); }
-    static PassOwnPtr<ElementAttributeData> createImmutable(const Vector<Attribute>&);
+    static PassRefPtr<ElementAttributeData> create() { return adoptRef(new ElementAttributeData); }
+    static PassRefPtr<ElementAttributeData> createImmutable(const Vector<Attribute>&);
     ~ElementAttributeData();
 
     void clearClass() { m_classNames.clear(); }
@@ -110,16 +110,16 @@
     void clearAttributes(Element*);
 
     bool isMutable() const { return m_isMutable; }
-    PassOwnPtr<ElementAttributeData> makeMutable() const { return adoptPtr(new ElementAttributeData(*this)); }
+    PassRefPtr<ElementAttributeData> makeMutable() const { return adoptRef(new ElementAttributeData(*this)); }
 
+    unsigned m_isMutable : 1;
+    unsigned m_arraySize : 31;
+
     mutable RefPtr<StylePropertySet> m_inlineStyleDecl;
     mutable RefPtr<StylePropertySet> m_attributeStyle;
     mutable SpaceSplitString m_classNames;
     mutable AtomicString m_idForStyleResolution;
 
-    unsigned m_isMutable : 1;
-    unsigned m_arraySize : 31;
-
     union {
         Vector<Attribute, 4>* m_mutableAttributeVector;
         void* m_attributes;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to