Title: [115379] trunk/Source/WebCore
Revision
115379
Author
an...@apple.com
Date
2012-04-26 15:26:35 -0700 (Thu, 26 Apr 2012)

Log Message

Cache parsed stylesheets
https://bugs.webkit.org/show_bug.cgi?id=85004

Reviewed by Andreas Kling.

CSS parsing is 1-2% of WebKit CPU usage on average pages, more on sites with large stylesheets.
We currently reparse all stylesheets from source text when they are encountered again. In many
browsing scenarios we can eliminate lot of this by caching the parsed stylesheets. For example 
it is very common for subpages of a site to share the stylesheets.
        
This patch enables memory caching for stylesheet loaded using the <link> element. Only stylesheets
that have no import rules are cacheable for now.
        
Cached stylesheets are copied on restore so there is no sharing (and no memory wins) yet.
In the future we will also be able to share the actual data structures between pages for 
significant memory savings.
        
After browsing around for a while <5% of the memory cache data was in parsed stylesheets so this
does not bloat the cache significantly.

* css/CSSStyleSheet.cpp:
(WebCore):
(WebCore::StyleSheetInternal::estimatedSizeInBytes):
        
    Estimate stylesheet size so we can handle decoded data pruning correctly.

* css/CSSStyleSheet.h:
(StyleSheetInternal):
* css/StylePropertySet.cpp:
(WebCore::StylePropertySet::averageSizeInBytes):
(WebCore):
* css/StylePropertySet.h:
(StylePropertySet):
* css/StyleRule.cpp:
(WebCore::StyleRule::averageSizeInBytes):
(WebCore):
* css/StyleRule.h:
(StyleRule):
* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::setCSSStyleSheet):
        
    Save and restore parsed stylesheet. The current CSS parse context must be identical to the cached 
    stylesheets. This ensures that the parsing results would be identical.

* loader/cache/CachedCSSStyleSheet.cpp:
(WebCore):
(WebCore::CachedCSSStyleSheet::destroyDecodedData):
(WebCore::CachedCSSStyleSheet::restoreParsedStyleSheet):
(WebCore::CachedCSSStyleSheet::saveParsedStyleSheet):
* loader/cache/CachedCSSStyleSheet.h:
        
    The parsed stylesheet cache is considered decoded data, similar to the image bitmaps. It uses the
    same mechanism for pruning.

(WebCore):
(CachedCSSStyleSheet):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (115378 => 115379)


--- trunk/Source/WebCore/ChangeLog	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/ChangeLog	2012-04-26 22:26:35 UTC (rev 115379)
@@ -1,3 +1,62 @@
+2012-04-26  Antti Koivisto  <an...@apple.com>
+
+        Cache parsed stylesheets
+        https://bugs.webkit.org/show_bug.cgi?id=85004
+
+        Reviewed by Andreas Kling.
+
+        CSS parsing is 1-2% of WebKit CPU usage on average pages, more on sites with large stylesheets.
+        We currently reparse all stylesheets from source text when they are encountered again. In many
+        browsing scenarios we can eliminate lot of this by caching the parsed stylesheets. For example 
+        it is very common for subpages of a site to share the stylesheets.
+        
+        This patch enables memory caching for stylesheet loaded using the <link> element. Only stylesheets
+        that have no import rules are cacheable for now.
+        
+        Cached stylesheets are copied on restore so there is no sharing (and no memory wins) yet.
+        In the future we will also be able to share the actual data structures between pages for 
+        significant memory savings.
+        
+        After browsing around for a while <5% of the memory cache data was in parsed stylesheets so this
+        does not bloat the cache significantly.
+
+        * css/CSSStyleSheet.cpp:
+        (WebCore):
+        (WebCore::StyleSheetInternal::estimatedSizeInBytes):
+        
+            Estimate stylesheet size so we can handle decoded data pruning correctly.
+
+        * css/CSSStyleSheet.h:
+        (StyleSheetInternal):
+        * css/StylePropertySet.cpp:
+        (WebCore::StylePropertySet::averageSizeInBytes):
+        (WebCore):
+        * css/StylePropertySet.h:
+        (StylePropertySet):
+        * css/StyleRule.cpp:
+        (WebCore::StyleRule::averageSizeInBytes):
+        (WebCore):
+        * css/StyleRule.h:
+        (StyleRule):
+        * html/HTMLLinkElement.cpp:
+        (WebCore::HTMLLinkElement::setCSSStyleSheet):
+        
+            Save and restore parsed stylesheet. The current CSS parse context must be identical to the cached 
+            stylesheets. This ensures that the parsing results would be identical.
+
+        * loader/cache/CachedCSSStyleSheet.cpp:
+        (WebCore):
+        (WebCore::CachedCSSStyleSheet::destroyDecodedData):
+        (WebCore::CachedCSSStyleSheet::restoreParsedStyleSheet):
+        (WebCore::CachedCSSStyleSheet::saveParsedStyleSheet):
+        * loader/cache/CachedCSSStyleSheet.h:
+        
+            The parsed stylesheet cache is considered decoded data, similar to the image bitmaps. It uses the
+            same mechanism for pruning.
+
+        (WebCore):
+        (CachedCSSStyleSheet):
+
 2012-04-26  Anders Carlsson  <ander...@apple.com>
 
         A TileCache should never outlive its WebTileCacheLayer

Modified: trunk/Source/WebCore/css/CSSStyleSheet.cpp (115378 => 115379)


--- trunk/Source/WebCore/css/CSSStyleSheet.cpp	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/CSSStyleSheet.cpp	2012-04-26 22:26:35 UTC (rev 115379)
@@ -72,6 +72,24 @@
 }
 #endif
 
+// Rough size estimate for the memory cache.
+unsigned StyleSheetInternal::estimatedSizeInBytes() const
+{
+    // Note that this does not take into account size of the strings hanging from various objects. 
+    // The assumption is that nearly all of of them are atomic and would exist anyway.
+    unsigned size = sizeof(*this);
+
+    // FIXME: This ignores the children of media and region rules.
+    // Most rules are StyleRules.
+    size += ruleCount() * StyleRule::averageSizeInBytes();
+
+    for (unsigned i = 0; i < m_importRules.size(); ++i) {
+        if (StyleSheetInternal* sheet = m_importRules[i]->styleSheet())
+            size += sheet->estimatedSizeInBytes();
+    }
+    return size;
+}
+
 StyleSheetInternal::StyleSheetInternal(StyleRuleImport* ownerRule, const String& originalURL, const KURL& finalURL, const CSSParserContext& context)
     : m_ownerRule(ownerRule)
     , m_originalURL(originalURL)

Modified: trunk/Source/WebCore/css/CSSStyleSheet.h (115378 => 115379)


--- trunk/Source/WebCore/css/CSSStyleSheet.h	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/CSSStyleSheet.h	2012-04-26 22:26:35 UTC (rev 115379)
@@ -127,6 +127,8 @@
     unsigned ruleCount() const;
     
     bool usesRemUnits() const { return m_usesRemUnits; }
+
+    unsigned estimatedSizeInBytes() const;
     
     bool wrapperInsertRule(PassRefPtr<StyleRuleBase>, unsigned index);
     void wrapperDeleteRule(unsigned index);

Modified: trunk/Source/WebCore/css/StylePropertySet.cpp (115378 => 115379)


--- trunk/Source/WebCore/css/StylePropertySet.cpp	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/StylePropertySet.cpp	2012-04-26 22:26:35 UTC (rev 115379)
@@ -1000,6 +1000,13 @@
     propertySetCSSOMWrapperMap().get(this)->clearParentElement();
 }
 
+unsigned StylePropertySet::averageSizeInBytes()
+{
+    // Please update this if the storage scheme changes so that this longer reflects the actual size.
+    return sizeof(StylePropertySet);
+}
+
+// See the function above if you need to update this.
 class SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
     Vector<CSSProperty, 4> properties;
     unsigned bitfield;

Modified: trunk/Source/WebCore/css/StylePropertySet.h (115378 => 115379)


--- trunk/Source/WebCore/css/StylePropertySet.h	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/StylePropertySet.h	2012-04-26 22:26:35 UTC (rev 115379)
@@ -113,6 +113,8 @@
     // FIXME: Expand the concept of mutable/immutable StylePropertySet.
     bool isMutable() const { return m_ownsCSSOMWrapper; }
 
+    static unsigned averageSizeInBytes();
+
 private:
     StylePropertySet(CSSParserMode);
     StylePropertySet(const Vector<CSSProperty>&);

Modified: trunk/Source/WebCore/css/StyleRule.cpp (115378 => 115379)


--- trunk/Source/WebCore/css/StyleRule.cpp	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/StyleRule.cpp	2012-04-26 22:26:35 UTC (rev 115379)
@@ -143,6 +143,11 @@
     return rule.release();
 }
 
+unsigned StyleRule::averageSizeInBytes()
+{
+    return sizeof(StyleRule) + StylePropertySet::averageSizeInBytes();
+}
+
 StyleRule::StyleRule(int sourceLine)
     : StyleRuleBase(Style, sourceLine)
 {

Modified: trunk/Source/WebCore/css/StyleRule.h (115378 => 115379)


--- trunk/Source/WebCore/css/StyleRule.h	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/css/StyleRule.h	2012-04-26 22:26:35 UTC (rev 115379)
@@ -102,6 +102,8 @@
 
     PassRefPtr<StyleRule> copy() const { return adoptRef(new StyleRule(*this)); }
 
+    static unsigned averageSizeInBytes();
+
 private:
     StyleRule(int sourceLine);
     StyleRule(const StyleRule&);

Modified: trunk/Source/WebCore/html/HTMLLinkElement.cpp (115378 => 115379)


--- trunk/Source/WebCore/html/HTMLLinkElement.cpp	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/html/HTMLLinkElement.cpp	2012-04-26 22:26:35 UTC (rev 115379)
@@ -295,6 +295,23 @@
     }
 
     CSSParserContext parserContext(document(), baseURL, charset);
+
+    if (RefPtr<StyleSheetInternal> restoredSheet = const_cast<CachedCSSStyleSheet*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext)) {
+        ASSERT(restoredSheet->isCacheable());
+        ASSERT(!restoredSheet->isLoading());
+
+        // restoreParsedStyleSheet() currently returns a copy so it is ok to mutate the queries and the title like this.
+        RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(m_media);
+        restoredSheet->setMediaQueries(media.release());
+        restoredSheet->setTitle(title());
+
+        m_sheet = CSSStyleSheet::create(restoredSheet, this);
+        m_loading = false;
+        sheetLoaded();
+        notifyLoadedSheetAndAllCriticalSubresources(false);
+        return;
+    }
+
     RefPtr<StyleSheetInternal> styleSheet = StyleSheetInternal::create(href, baseURL, parserContext);
     m_sheet = CSSStyleSheet::create(styleSheet, this);
 
@@ -307,6 +324,9 @@
     m_loading = false;
     styleSheet->notifyLoadedSheet(cachedStyleSheet);
     styleSheet->checkLoaded();
+
+    if (styleSheet->isCacheable())
+        const_cast<CachedCSSStyleSheet*>(cachedStyleSheet)->saveParsedStyleSheet(styleSheet);
 }
 
 bool HTMLLinkElement::styleSheetIsLoading() const

Modified: trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp (115378 => 115379)


--- trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.cpp	2012-04-26 22:26:35 UTC (rev 115379)
@@ -27,12 +27,14 @@
 #include "config.h"
 #include "CachedCSSStyleSheet.h"
 
-#include "MemoryCache.h"
+#include "CSSStyleSheet.h"
 #include "CachedResourceClientWalker.h"
 #include "CachedStyleSheetClient.h"
 #include "HTTPParsers.h"
+#include "MemoryCache.h"
+#include "SharedBuffer.h"
 #include "TextResourceDecoder.h"
-#include "SharedBuffer.h"
+#include <wtf/CurrentTime.h>
 #include <wtf/Vector.h>
 
 namespace WebCore {
@@ -148,5 +150,38 @@
         return true;
     return typeOK;
 }
- 
+
+void CachedCSSStyleSheet::destroyDecodedData()
+{
+    m_parsedStyleSheetCache.clear();
+    setDecodedSize(0);
 }
+
+PassRefPtr<StyleSheetInternal> CachedCSSStyleSheet::restoreParsedStyleSheet(const CSSParserContext& context)
+{
+    if (!m_parsedStyleSheetCache)
+        return 0;
+    // Cached parsed stylesheet has mutated, kick it out.
+    if (!m_parsedStyleSheetCache->isCacheable()) {
+        m_parsedStyleSheetCache.clear();
+        setDecodedSize(0);
+        return 0;
+    }
+    // Contexts must be identical so we know we would get the same exact result if we parsed again.
+    if (m_parsedStyleSheetCache->parserContext() != context)
+        return 0;
+
+    didAccessDecodedData(currentTime());
+    // FIXME: Implement copy-on-write to avoid copying when not necessary.
+    return m_parsedStyleSheetCache->copy();
+}
+
+void CachedCSSStyleSheet::saveParsedStyleSheet(PassRefPtr<StyleSheetInternal> sheet)
+{
+    ASSERT(sheet && sheet->isCacheable());
+    m_parsedStyleSheetCache = sheet;
+
+    setDecodedSize(m_parsedStyleSheetCache->estimatedSizeInBytes());
+}
+
+}

Modified: trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.h (115378 => 115379)


--- trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.h	2012-04-26 22:25:35 UTC (rev 115378)
+++ trunk/Source/WebCore/loader/cache/CachedCSSStyleSheet.h	2012-04-26 22:26:35 UTC (rev 115379)
@@ -33,7 +33,9 @@
 
     class CachedResourceClient;
     class SharedBuffer;
+    class StyleSheetInternal;
     class TextResourceDecoder;
+    struct CSSParserContext;
 
     class CachedCSSStyleSheet : public CachedResource {
     public:
@@ -51,7 +53,12 @@
         virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
         virtual void error(CachedResource::Status);
 
+        virtual void destroyDecodedData() OVERRIDE;
+
         void checkNotify();
+
+        PassRefPtr<StyleSheetInternal> restoreParsedStyleSheet(const CSSParserContext&);
+        void saveParsedStyleSheet(PassRefPtr<StyleSheetInternal>);
     
     private:
         bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const;
@@ -60,6 +67,8 @@
     protected:
         RefPtr<TextResourceDecoder> m_decoder;
         String m_decodedSheetText;
+
+        RefPtr<StyleSheetInternal> m_parsedStyleSheetCache;
     };
 
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to