- 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;