Title: [110316] trunk/Source/WebCore
Revision
110316
Author
[email protected]
Date
2012-03-09 12:16:02 -0800 (Fri, 09 Mar 2012)

Log Message

Presentation attribute cache
https://bugs.webkit.org/show_bug.cgi?id=80707
        
Reviewed by Andreas Kling.

It is common for the same presentation attribute values repeat. We should introduce a cache that uses 
presentation attribute names and values as key. This will help to avoid repeated parsing of the 
same attribute values, reduce memory consumption and speed up the style resolve.
        
This patch introduces a simple and small (128 entries) global cache. In general web browsing it seems
to give sharing rate of >75% (an average presentation attribute property set is shared between >4 elements).

* dom/StyledElement.cpp:
(WebCore::PresentationAttributeCacheKey::PresentationAttributeCacheKey):
(PresentationAttributeCacheKey):
(WebCore):
(WebCore::operator!=):
(PresentationAttributeCacheEntry):
(WebCore::presentationAttributeCache):
(WebCore::attributeNameSort):
(WebCore::StyledElement::makePresentationAttributeCacheKey):
(WebCore::computePresentationAttributeCacheHash):
(WebCore::StyledElement::updateAttributeStyle):
* dom/StyledElement.h:
(WebCore):
(StyledElement):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (110315 => 110316)


--- trunk/Source/WebCore/ChangeLog	2012-03-09 20:12:35 UTC (rev 110315)
+++ trunk/Source/WebCore/ChangeLog	2012-03-09 20:16:02 UTC (rev 110316)
@@ -1,3 +1,32 @@
+2012-03-09  Antti Koivisto  <[email protected]>
+
+        Presentation attribute cache
+        https://bugs.webkit.org/show_bug.cgi?id=80707
+        
+        Reviewed by Andreas Kling.
+
+        It is common for the same presentation attribute values repeat. We should introduce a cache that uses 
+        presentation attribute names and values as key. This will help to avoid repeated parsing of the 
+        same attribute values, reduce memory consumption and speed up the style resolve.
+        
+        This patch introduces a simple and small (128 entries) global cache. In general web browsing it seems
+        to give sharing rate of >75% (an average presentation attribute property set is shared between >4 elements).
+
+        * dom/StyledElement.cpp:
+        (WebCore::PresentationAttributeCacheKey::PresentationAttributeCacheKey):
+        (PresentationAttributeCacheKey):
+        (WebCore):
+        (WebCore::operator!=):
+        (PresentationAttributeCacheEntry):
+        (WebCore::presentationAttributeCache):
+        (WebCore::attributeNameSort):
+        (WebCore::StyledElement::makePresentationAttributeCacheKey):
+        (WebCore::computePresentationAttributeCacheHash):
+        (WebCore::StyledElement::updateAttributeStyle):
+        * dom/StyledElement.h:
+        (WebCore):
+        (StyledElement):
+
 2012-03-08  Erik Arvidsson  <[email protected]>
 
         [V8] Fix object scope for inline event attribute handlers

Modified: trunk/Source/WebCore/dom/StyledElement.cpp (110315 => 110316)


--- trunk/Source/WebCore/dom/StyledElement.cpp	2012-03-09 20:12:35 UTC (rev 110315)
+++ trunk/Source/WebCore/dom/StyledElement.cpp	2012-03-09 20:16:02 UTC (rev 110316)
@@ -46,6 +46,33 @@
 
 using namespace HTMLNames;
 
+struct PresentationAttributeCacheKey {
+    PresentationAttributeCacheKey() : tagName(0) { }
+    AtomicStringImpl* tagName;
+    // Only the values need refcounting.
+    Vector<pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues;
+};
+
+struct PresentationAttributeCacheEntry {
+    PresentationAttributeCacheKey key;
+    RefPtr<StylePropertySet> value;
+};
+
+typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache;
+    
+static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b)
+{
+    if (a.tagName != b.tagName)
+        return true;
+    return a.attributesAndValues != b.attributesAndValues;
+}
+
+static PresentationAttributeCache& presentationAttributeCache()
+{
+    DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ());
+    return cache;
+}
+
 void StyledElement::updateStyleAttribute() const
 {
     ASSERT(!isStyleAttributeValid());
@@ -157,21 +184,98 @@
         inlineStyle->addSubresourceStyleURLs(urls, document()->elementSheet());
 }
 
-void StyledElement::updateAttributeStyle()
+static inline bool attributeNameSort(const pair<AtomicStringImpl*, AtomicString>& p1, const pair<AtomicStringImpl*, AtomicString>& p2)
 {
-    RefPtr<StylePropertySet> style = StylePropertySet::create();
-    for (unsigned i = 0; i < attributeCount(); ++i) {
+    // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. 
+    return p1.first < p2.first;
+}
+
+void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const
+{    
+    // FIXME: Enable for SVG.
+    if (namespaceURI() != xhtmlNamespaceURI)
+        return;
+    // Interpretation of the size attributes on <input> depends on the type attribute.
+    if (hasTagName(inputTag))
+        return;
+    unsigned size = attributeCount();
+    for (unsigned i = 0; i < size; ++i) {
         Attribute* attribute = attributeItem(i);
-        collectStyleForAttribute(attribute, style.get());
+        if (!isPresentationAttribute(attribute->name()))
+            continue;
+        if (!attribute->namespaceURI().isNull())
+            return;
+        // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
+        if (attribute->name() == backgroundAttr)
+            return;
+        result.attributesAndValues.append(make_pair(attribute->localName().impl(), attribute->value()));
     }
+    if (result.attributesAndValues.isEmpty())
+        return;
+    // Attribute order doesn't matter. Sort for easy equality comparison.
+    std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort);
+    // The cache key is non-null when the tagName is set.
+    result.tagName = localName().impl();
+}
+
+static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key)
+{
+    if (!key.tagName)
+        return 0;
+    ASSERT(key.attributesAndValues.size());
+    unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0]));
+    return WTF::intHash((static_cast<uint64_t>(key.tagName->existingHash()) << 32 | attributeHash));
+}
+
+void StyledElement::updateAttributeStyle()
+{
+    PresentationAttributeCacheKey cacheKey;
+    makePresentationAttributeCacheKey(cacheKey);
+
+    unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey);
+
+    PresentationAttributeCache::iterator cacheIterator;
+    if (cacheHash) {
+        cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).first;
+        if (cacheIterator->second && cacheIterator->second->key != cacheKey)
+            cacheHash = 0;
+    } else
+        cacheIterator = presentationAttributeCache().end();
+
+    RefPtr<StylePropertySet> style;
+    if (cacheHash && cacheIterator->second) 
+        style = cacheIterator->second->value;
+    else {
+        style = StylePropertySet::create();
+        unsigned size = attributeCount();
+        for (unsigned i = 0; i < size; ++i) {
+            Attribute* attribute = attributeItem(i);
+            collectStyleForAttribute(attribute, style.get());
+        }
+    }
     clearAttributeStyleDirty();
 
     if (style->isEmpty())
         attributeData()->setAttributeStyle(0);
     else {
         style->shrinkToFit();
-        attributeData()->setAttributeStyle(style.release());
+        attributeData()->setAttributeStyle(style);
     }
+
+    if (!cacheHash || cacheIterator->second)
+        return;
+
+    OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry);
+    newEntry->key = cacheKey;
+    newEntry->value = style.release();
+
+    static const int presentationAttributeCacheMaximumSize = 128;
+    if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) {
+        // Start building from scratch if the cache ever gets big.
+        presentationAttributeCache().clear();
+        presentationAttributeCache().set(cacheHash, newEntry.release());
+    } else
+        cacheIterator->second = newEntry.release();
 }
 
 void StyledElement::addPropertyToAttributeStyle(StylePropertySet* style, int propertyID, int identifier)

Modified: trunk/Source/WebCore/dom/StyledElement.h (110315 => 110316)


--- trunk/Source/WebCore/dom/StyledElement.h	2012-03-09 20:12:35 UTC (rev 110315)
+++ trunk/Source/WebCore/dom/StyledElement.h	2012-03-09 20:16:02 UTC (rev 110316)
@@ -31,6 +31,7 @@
 namespace WebCore {
 
 class Attribute;
+struct PresentationAttributeCacheKey;
 
 class StyledElement : public Element {
 public:
@@ -82,6 +83,7 @@
     virtual void updateStyleAttribute() const;
     void inlineStyleChanged();
 
+    void makePresentationAttributeCacheKey(PresentationAttributeCacheKey&) const;
     void updateAttributeStyle();
 
     void destroyInlineStyle()
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to