Use purgeable memory to keep more dead resources in cache
authorggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Sep 2010 00:18:03 +0000 (00:18 +0000)
committerggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Sep 2010 00:18:03 +0000 (00:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=44806
<rdar://problem/8350901>

Patch by Pratik Solanki <psolanki@apple.com> on 2010-09-21
Reviewed by Geoffrey Garen and Darin Adler.

This changes the behavior of dead resources in the WebCore cache to be the following if
shouldMakeResourcePurgeableOnEviction() returns true.

1. Dead resources in the cache are kept in non-purgeable memory.
2. When we prune dead resources, instead of freeing them, we mark their memory as purgeable
and keep the resources until the kernel reclaims the purgeable memory.

By leaving the in-cache dead resources in dirty resident memory, we decrease the likelihood
of the kernel claiming that memory and forcing us to refetch the resource (for example when
a user presses back).

And by having an unbounded number of resource objects using purgeable memory, we can use
as much memory as is available on the machine. The trade-off is that the CachedResource
object (and its member variables) are allocated in non-purgeable TC-malloc'd memory so
we would see slightly more memory use due to this.

* loader/Cache.cpp:
(WebCore::Cache::resourceForURL): Adjust sizes appropriately if we made resource memory
non-purgeable.
(WebCore::Cache::pruneDeadResources): When removing dead resources, try first to mark their
memory as purgeable. If not, evict the resource.
(WebCore::Cache::makeResourcePurgeable): Added. Try to mark resource
memory as purgeable. If successful, adjust the sizes so that we don't
factor this resources size in the Cache size calculation.
(WebCore::Cache::evict): Don't decrement size if we already did it in makeResourcePurgeable.
(WebCore::Cache::dumpLRULists): Extra debug logging.
* loader/Cache.h:
(WebCore::Cache::shouldMakeResourcePurgeableOnEviction): Added. Indicates if the new
behaviour is enabled.
* loader/CachedCSSStyleSheet.cpp:
(WebCore::CachedCSSStyleSheet::allClientsRemoved): Do not mark memory as purgeable. The
Cache class takes care of this.
* loader/CachedImage.cpp:
(WebCore::CachedImage::destroyDecodedData): Ditto.
* loader/CachedScript.cpp:
(WebCore::CachedScript::destroyDecodedData): Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@67996 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/loader/Cache.cpp
WebCore/loader/Cache.h
WebCore/loader/CachedCSSStyleSheet.cpp
WebCore/loader/CachedImage.cpp
WebCore/loader/CachedScript.cpp

index 6595a15..9f76436 100644 (file)
@@ -1,3 +1,48 @@
+2010-09-21  Pratik Solanki  <psolanki@apple.com>
+
+        Reviewed by Geoffrey Garen and Darin Adler.
+
+        Use purgeable memory to keep more dead resources in cache
+        https://bugs.webkit.org/show_bug.cgi?id=44806
+        <rdar://problem/8350901>
+
+        This changes the behavior of dead resources in the WebCore cache to be the following if
+        shouldMakeResourcePurgeableOnEviction() returns true.
+
+        1. Dead resources in the cache are kept in non-purgeable memory.
+        2. When we prune dead resources, instead of freeing them, we mark their memory as purgeable
+        and keep the resources until the kernel reclaims the purgeable memory.
+
+        By leaving the in-cache dead resources in dirty resident memory, we decrease the likelihood
+        of the kernel claiming that memory and forcing us to refetch the resource (for example when
+        a user presses back).
+
+        And by having an unbounded number of resource objects using purgeable memory, we can use
+        as much memory as is available on the machine. The trade-off is that the CachedResource
+        object (and its member variables) are allocated in non-purgeable TC-malloc'd memory so
+        we would see slightly more memory use due to this.
+
+        * loader/Cache.cpp:
+        (WebCore::Cache::resourceForURL): Adjust sizes appropriately if we made resource memory
+        non-purgeable.
+        (WebCore::Cache::pruneDeadResources): When removing dead resources, try first to mark their
+        memory as purgeable. If not, evict the resource.
+        (WebCore::Cache::makeResourcePurgeable): Added. Try to mark resource
+        memory as purgeable. If successful, adjust the sizes so that we don't
+        factor this resources size in the Cache size calculation.
+        (WebCore::Cache::evict): Don't decrement size if we already did it in makeResourcePurgeable.
+        (WebCore::Cache::dumpLRULists): Extra debug logging.
+        * loader/Cache.h:
+        (WebCore::Cache::shouldMakeResourcePurgeableOnEviction): Added. Indicates if the new
+        behaviour is enabled.
+        * loader/CachedCSSStyleSheet.cpp:
+        (WebCore::CachedCSSStyleSheet::allClientsRemoved): Do not mark memory as purgeable. The
+        Cache class takes care of this.
+        * loader/CachedImage.cpp:
+        (WebCore::CachedImage::destroyDecodedData): Ditto.
+        * loader/CachedScript.cpp:
+        (WebCore::CachedScript::destroyDecodedData): Ditto.
+
 2010-09-21  Dan Bernstein  <mitz@apple.com>
 
         Reviewed by Darin Adler.
index dbcec00..647dfac 100644 (file)
@@ -238,11 +238,15 @@ void Cache::revalidationFailed(CachedResource* revalidatingResource)
 CachedResource* Cache::resourceForURL(const String& url)
 {
     CachedResource* resource = m_resources.get(url);
+    bool wasPurgeable = Cache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable();
     if (resource && !resource->makePurgeable(false)) {
         ASSERT(!resource->hasClients());
         evict(resource);
         return 0;
     }
+    // Add the size back since we had subtracted it when we marked the memory as purgeable.
+    if (wasPurgeable)
+        adjustSize(resource->hasClients(), resource->size());
     return resource;
 }
 
@@ -363,7 +367,9 @@ void Cache::pruneDeadResources()
         while (current) {
             CachedResource* prev = current->m_prevInAllResourcesList;
             if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
-                evict(current);
+                if (!makeResourcePurgeable(current))
+                    evict(current);
+
                 // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an
                 // SVG CachedImage that has subresources.
                 if (!m_inPruneDeadResources)
@@ -397,6 +403,25 @@ void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned
     prune();
 }
 
+bool Cache::makeResourcePurgeable(CachedResource* resource)
+{
+    if (!Cache::shouldMakeResourcePurgeableOnEviction())
+        return false;
+
+    if (!resource->inCache())
+        return false;
+
+    if (!resource->isSafeToMakePurgeable())
+        return false;
+
+    if (!resource->makePurgeable(true))
+        return false;
+
+    adjustSize(resource->hasClients(), -resource->size());
+
+    return true;
+}
+
 void Cache::evict(CachedResource* resource)
 {
     // The resource may have already been removed by someone other than our caller,
@@ -410,10 +435,10 @@ void Cache::evict(CachedResource* resource)
         removeFromLRUList(resource);
         removeFromLiveDecodedResourcesList(resource);
 
-        // Subtract from our size totals.
-        int delta = -static_cast<int>(resource->size());
-        if (delta)
-            adjustSize(resource->hasClients(), delta);
+        // If the resource was purged, it means we had already decremented the size when we made the
+        // resource purgeable in makeResourcePurgeable().
+        if (!Cache::shouldMakeResourcePurgeableOnEviction() || !resource->wasPurged())
+            adjustSize(resource->hasClients(), -resource->size());
     } else
         ASSERT(m_resources.get(resource->url()) != resource);
 
@@ -725,7 +750,7 @@ void Cache::dumpStats()
 
 void Cache::dumpLRULists(bool includeLive) const
 {
-    printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n");
+    printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n");
 
     int size = m_allResources.size();
     for (int i = size - 1; i >= 0; i--) {
@@ -734,7 +759,8 @@ void Cache::dumpLRULists(bool includeLive) const
         while (current) {
             CachedResource* prev = current->m_prevInAllResourcesList;
             if (includeLive || !current->hasClients())
-                printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients());
+                printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged());
+
             current = prev;
         }
     }
index 5743696..14ddc21 100644 (file)
@@ -55,6 +55,22 @@ class KURL;
 // -------|-----+++++++++++++++|
 // -------|-----+++++++++++++++|+++++
 
+// The behavior of the cache changes in the following way if shouldMakeResourcePurgeableOnEviction
+// returns true.
+//
+// 1. Dead resources in the cache are kept in non-purgeable memory.
+// 2. When we prune dead resources, instead of freeing them, we mark their memory as purgeable and
+//    keep the resources until the kernel reclaims the purgeable memory.
+//
+// By leaving the in-cache dead resources in dirty resident memory, we decrease the likelihood of
+// the kernel claiming that memory and forcing us to refetch the resource (for example when a user
+// presses back).
+//
+// And by having an unbounded number of resource objects using purgeable memory, we can use as much
+// memory as is available on the machine. The trade-off here is that the CachedResource object (and
+// its member variables) are allocated in non-purgeable TC-malloc'd memory so we would see slightly
+// more memory use due to this.
+
 class Cache : public Noncopyable {
 public:
     friend Cache* cache();
@@ -148,6 +164,8 @@ public:
     void addToLiveResourcesSize(CachedResource*);
     void removeFromLiveResourcesSize(CachedResource*);
 
+    static bool shouldMakeResourcePurgeableOnEviction();
+
     // Function to collect cache statistics for the caches window in the Safari Debug menu.
     Statistics getStatistics();
 
@@ -168,6 +186,7 @@ private:
     void pruneDeadResources(); // Flush decoded and encoded data from resources not referenced by Web pages.
     void pruneLiveResources(); // Flush decoded data from resources still referenced by Web pages.
 
+    bool makeResourcePurgeable(CachedResource*);
     void evict(CachedResource*);
 
     // Member variables.
@@ -199,6 +218,15 @@ private:
     HashMap<String, CachedResource*> m_resources;
 };
 
+inline bool Cache::shouldMakeResourcePurgeableOnEviction()
+{
+#if PLATFORM(IOS)
+    return true;
+#else
+    return false;
+#endif
+}
+
 // Function to obtain the global cache.
 Cache* cache();
 
index 7866efd..877cd1d 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "CachedCSSStyleSheet.h"
 
+#include "Cache.h"
 #include "CachedResourceClient.h"
 #include "CachedResourceClientWalker.h"
 #include "HTTPParsers.h"
@@ -58,7 +59,7 @@ void CachedCSSStyleSheet::didAddClient(CachedResourceClient *c)
 
 void CachedCSSStyleSheet::allClientsRemoved()
 {
-    if (isSafeToMakePurgeable())
+    if (!Cache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
         makePurgeable(true);
 }
 
index 202f32c..2c18f35 100644 (file)
@@ -331,7 +331,8 @@ void CachedImage::destroyDecodedData()
         // Invoking addClient() will reconstruct the image object.
         m_image = 0;
         setDecodedSize(0);
-        makePurgeable(true);
+        if (!Cache::shouldMakeResourcePurgeableOnEviction())
+            makePurgeable(true);
     } else if (m_image && !errorOccurred())
         m_image->destroyDecodedData();
 }
index 58895d6..1898438 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "CachedScript.h"
 
+#include "Cache.h"
 #include "CachedResourceClient.h"
 #include "CachedResourceClientWalker.h"
 #include "SharedBuffer.h"
@@ -110,7 +111,7 @@ void CachedScript::destroyDecodedData()
 {
     m_script = String();
     setDecodedSize(0);
-    if (isSafeToMakePurgeable())
+    if (!Cache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable())
         makePurgeable(true);
 }