Bug 610417 - Optimize eviction in bfcache. r=smaug a=blocking-fennec
authorAlon Zakai <azakai@mozilla.com>
Wed, 19 Jan 2011 11:57:36 -0800
changeset 60876 7756b033258fe3f3079dd8c416e63d3eb1afd0b1
parent 60875 754ea885a0f87af2f0cfe1e8684db67f5603657b
child 60878 f4db6f267ddec2944daf4613ac5ca088b0bed8fb
push id18147
push userazakai@mozilla.com
push dateWed, 19 Jan 2011 20:01:54 +0000
treeherdermozilla-central@7756b033258f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, blocking-fennec
bugs610417
milestone2.0b10pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 610417 - Optimize eviction in bfcache. r=smaug a=blocking-fennec
browser/app/profile/firefox.js
docshell/shistory/public/nsISHEntry.idl
docshell/shistory/src/nsSHEntry.cpp
docshell/shistory/src/nsSHEntry.h
docshell/shistory/src/nsSHistory.cpp
modules/libpref/src/init/all.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -902,16 +902,18 @@ pref("browser.safebrowsing.enabled", fal
 pref("browser.safebrowsing.malware.enabled", false);
 
 // don't check for default browser
 pref("browser.shell.checkDefaultBrowser", false);
 
 // disable bfcache for memory
 pref("browser.sessionhistory.max_total_viewers", 0);
 
+pref("browser.sessionhistory.optimize_eviction", false);
+
 // tweak default content sink prefs
 pref("content.sink.interactive_deflect_count", 10); /* default 0 */
 pref("content.sink.perf_deflect_count", 50); /* default 200 */
 pref("content.sink.interactive_parse_time", 5000); /* default 3000 */
 pref("content.sink.perf_parse_time", 150000); /* default 360000 */
 pref("content.sink.pending_event_mode", 0); /* default 1 */
 pref("content.sink.event_probe_rate", 1); /* default 1 */
 pref("content.sink.interactive_time", 750000); /* default 750000 */
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -245,21 +245,26 @@ interface nsISHEntry : nsIHistoryEntry
     boolean hasDynamicallyAddedChild();
 
     /**
      * The history ID of the docshell.
      */
     attribute unsigned long long docshellID;
 };
 
-[scriptable, uuid(e0b0ac6d-cb29-4be2-b685-1755d6301a69)]
+[scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]
 interface nsISHEntryInternal : nsISupports
 {
     [notxpcom] void RemoveFromBFCacheAsync();
     [notxpcom] void RemoveFromBFCacheSync();
+
+    /**
+     * A number that is assigned by the sHistory when the entry is activated
+     */
+    attribute unsigned long lastTouched;
 };
 
 %{ C++
 // {BFD1A791-AD9F-11d3-BDC7-0050040A9B44}
 #define NS_SHENTRY_CID \
 {0xbfd1a791, 0xad9f, 0x11d3, {0xbd, 0xc7, 0x0, 0x50, 0x4, 0xa, 0x9b, 0x44}}
 
 #define NS_SHENTRY_CONTRACTID \
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -112,16 +112,17 @@ nsSHEntry::nsSHEntry()
   , mIsFrameNavigation(PR_FALSE)
   , mSaveLayoutState(PR_TRUE)
   , mExpired(PR_FALSE)
   , mSticky(PR_TRUE)
   , mDynamicallyCreated(PR_FALSE)
   , mParent(nsnull)
   , mViewerBounds(0, 0, 0, 0)
   , mDocShellID(0)
+  , mLastTouched(0)
 {
 }
 
 nsSHEntry::nsSHEntry(const nsSHEntry &other)
   : mURI(other.mURI)
   , mReferrerURI(other.mReferrerURI)
   // XXX why not copy mDocument?
   , mTitle(other.mTitle)
@@ -993,8 +994,23 @@ nsSHEntry::GetDocshellID(PRUint64* aID)
 
 NS_IMETHODIMP
 nsSHEntry::SetDocshellID(PRUint64 aID)
 {
   mDocShellID = aID;
   return NS_OK;
 }
 
+
+NS_IMETHODIMP
+nsSHEntry::GetLastTouched(unsigned int *aLastTouched)
+{
+  *aLastTouched = mLastTouched;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetLastTouched(unsigned int aLastTouched)
+{
+  mLastTouched = aLastTouched;
+  return NS_OK;
+}
+
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -115,11 +115,12 @@ private:
   nsIntRect                       mViewerBounds;
   nsCOMArray<nsIDocShellTreeItem> mChildShells;
   nsCOMPtr<nsISupportsArray>      mRefreshURIList;
   nsCOMPtr<nsISupports>           mOwner;
   nsExpirationState               mExpirationState;
   nsAutoPtr<nsDocShellEditorData> mEditorData;
   nsString                        mStateData;
   PRUint64                        mDocShellID;
+  PRUint32                        mLastTouched;
 };
 
 #endif /* nsSHEntry_h */
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -65,26 +65,37 @@
 #include "nsDocShell.h"
 
 // For calculating max history entries and max cachable contentviewers
 #include "nspr.h"
 #include <math.h>  // for log()
 
 #define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
 #define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
+#define PREF_SHISTORY_OPTIMIZE_EVICTION "browser.sessionhistory.optimize_eviction"
 
 static PRInt32  gHistoryMaxSize = 50;
 // Max viewers allowed per SHistory objects
 static const PRInt32  gHistoryMaxViewers = 3;
 // List of all SHistory objects, used for content viewer cache eviction
 static PRCList gSHistoryList;
 // Max viewers allowed total, across all SHistory objects - negative default
 // means we will calculate how many viewers to cache based on total memory
 PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
 
+// Whether we should optimize the search for which entry to evict,
+// by evicting older entries first. See entryLastTouched in
+// nsSHistory::EvictGlobalContentViewer().
+// NB: After 4.0, we should remove this option and the corresponding
+//     pref - optimization should always be used
+static PRBool gOptimizeEviction = PR_FALSE;
+// A counter that is used to be able to know the order in which
+// entries were touched, so that we can evict older entries first.
+static PRUint32 gTouchCounter = 0;
+
 enum HistCmd{
   HIST_CMD_BACK,
   HIST_CMD_FORWARD,
   HIST_CMD_GOTOINDEX,
   HIST_CMD_RELOAD
 } ;
 
 //*****************************************************************************
@@ -217,16 +228,18 @@ nsSHistory::CalcMaxTotalViewers()
 
 // static
 void
 nsSHistory::UpdatePrefs(nsIPrefBranch *aPrefBranch)
 {
   aPrefBranch->GetIntPref(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
   aPrefBranch->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
                           &sHistoryMaxTotalViewers);
+  aPrefBranch->GetBoolPref(PREF_SHISTORY_OPTIMIZE_EVICTION,
+                          &gOptimizeEviction);
   // If the pref is negative, that means we calculate how many viewers
   // we think we should cache, based on total memory
   if (sHistoryMaxTotalViewers < 0) {
     sHistoryMaxTotalViewers = CalcMaxTotalViewers();
   }
 }
 
 // static
@@ -260,16 +273,18 @@ nsSHistory::Startup()
     if (branch) {
       nsSHistoryObserver* obs = new nsSHistoryObserver();
       if (!obs) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       branch->AddObserver(PREF_SHISTORY_SIZE, obs, PR_FALSE);
       branch->AddObserver(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
                           obs, PR_FALSE);
+      branch->AddObserver(PREF_SHISTORY_OPTIMIZE_EVICTION,
+                          obs, PR_FALSE);
 
       nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();
       if (obsSvc) {
         // Observe empty-cache notifications so tahat clearing the disk/memory
         // cache will also evict all content viewers.
         obsSvc->AddObserver(obs,
                             NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, PR_FALSE);
@@ -932,16 +947,17 @@ nsSHistory::EvictGlobalContentViewer()
   PRBool shouldTryEviction = PR_TRUE;
   while (shouldTryEviction) {
     // Walk through our list of SHistory objects, looking for content
     // viewers in the possible active window of all of the SHEntry objects.
     // Keep track of the SHEntry object that has a ContentViewer and is
     // farthest from the current focus in any SHistory object.  The
     // ContentViewer associated with that SHEntry will be evicted
     PRInt32 distanceFromFocus = 0;
+    PRUint32 candidateLastTouched = 0;
     nsCOMPtr<nsISHEntry> evictFromSHE;
     nsCOMPtr<nsIContentViewer> evictViewer;
     PRInt32 totalContentViewers = 0;
     nsSHistory* shist = static_cast<nsSHistory*>
                                    (PR_LIST_HEAD(&gSHistoryList));
     while (shist != &gSHistoryList) {
       // Calculate the window of SHEntries that could possibly have a content
       // viewer.  There could be up to gHistoryMaxViewers content viewers,
@@ -956,16 +972,25 @@ nsSHistory::EvictGlobalContentViewer()
       for (PRInt32 i = startIndex; i <= endIndex; ++i) {
         nsCOMPtr<nsISHEntry> entry;
         trans->GetSHEntry(getter_AddRefs(entry));
         nsCOMPtr<nsIContentViewer> viewer;
         nsCOMPtr<nsISHEntry> ownerEntry;
         entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
                                    getter_AddRefs(viewer));
 
+        PRUint32 entryLastTouched = 0;
+        if (gOptimizeEviction) {
+          nsCOMPtr<nsISHEntryInternal> entryInternal = do_QueryInterface(entry);
+          if (entryInternal) {
+            // Find when this entry was last activated
+            entryInternal->GetLastTouched(&entryLastTouched);
+          }
+        }
+
 #ifdef DEBUG_PAGE_CACHE
         nsCOMPtr<nsIURI> uri;
         if (ownerEntry) {
           ownerEntry->GetURI(getter_AddRefs(uri));
         } else {
           entry->GetURI(getter_AddRefs(uri));
         }
         nsCAutoString spec;
@@ -980,22 +1005,26 @@ nsSHistory::EvictGlobalContentViewer()
         if (viewer) {
           PRInt32 distance = PR_ABS(shist->mIndex - i);
           
 #ifdef DEBUG_PAGE_CACHE
           printf("Has a cached content viewer: %s\n", spec.get());
           printf("mIndex: %d i: %d\n", shist->mIndex, i);
 #endif
           totalContentViewers++;
-          if (distance > distanceFromFocus) {
-            
+
+          // If this entry is further away from focus than any previously found
+          // or at the same distance but it is longer time since it was activated
+          // then take this entry as the new candiate for eviction
+          if (distance > distanceFromFocus || (distance == distanceFromFocus && candidateLastTouched > entryLastTouched)) {
+
 #ifdef DEBUG_PAGE_CACHE
             printf("Choosing as new eviction candidate: %s\n", spec.get());
 #endif
-
+            candidateLastTouched = entryLastTouched;
             distanceFromFocus = distance;
             evictFromSHE = ownerEntry;
             evictViewer = viewer;
           }
         }
         nsISHTransaction* temp = trans;
         temp->GetNext(getter_AddRefs(trans));
       }
@@ -1399,31 +1428,37 @@ nsSHistory::LoadNextPossibleEntry(PRInt3
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsSHistory::LoadEntry(PRInt32 aIndex, long aLoadType, PRUint32 aHistCmd)
 {
   nsCOMPtr<nsIDocShell> docShell;
-  nsCOMPtr<nsISHEntry> shEntry;
   // Keep note of requested history index in mRequestedIndex.
   mRequestedIndex = aIndex;
 
   nsCOMPtr<nsISHEntry> prevEntry;
   GetEntryAtIndex(mIndex, PR_FALSE, getter_AddRefs(prevEntry));
 
   nsCOMPtr<nsISHEntry> nextEntry;
   GetEntryAtIndex(mRequestedIndex, PR_FALSE, getter_AddRefs(nextEntry));
   nsCOMPtr<nsIHistoryEntry> nHEntry(do_QueryInterface(nextEntry));
   if (!nextEntry || !prevEntry || !nHEntry) {
     mRequestedIndex = -1;
     return NS_ERROR_FAILURE;
   }
 
+  // Remember that this entry is getting loaded at this point in the sequence
+  nsCOMPtr<nsISHEntryInternal> entryInternal = do_QueryInterface(nextEntry);
+
+  if (entryInternal) {
+    entryInternal->SetLastTouched(++gTouchCounter);
+  }
+
   // Send appropriate listener notifications
   PRBool canNavigate = PR_TRUE;
   // Get the uri for the entry we are about to visit
   nsCOMPtr<nsIURI> nextURI;
   nHEntry->GetURI(getter_AddRefs(nextURI));
 
   if(mListener) {
     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -100,16 +100,18 @@ pref("offline-apps.quota.warn",         
 pref("dom.indexedDB.enabled", true);
 // Space to allow indexedDB databases before prompting (in MB).
 pref("dom.indexedDB.warningQuota", 50);
 
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
+pref("browser.sessionhistory.optimize_eviction", false);
+
 pref("ui.use_native_colors", true);
 pref("ui.use_native_popup_windows", false);
 pref("ui.click_hold_context_menus", false);
 pref("browser.display.use_document_fonts",  1);  // 0 = never, 1 = quick, 2 = always
 pref("browser.display.use_document_colors", true);
 pref("browser.display.use_system_colors",   false);
 pref("browser.display.foreground_color",    "#000000");
 pref("browser.display.background_color",    "#FFFFFF");