Bug 1547452 - LSNG: Refactor Datastore::GetSnapshotInitInfo using a switch statement; r=asuth
authorJan Varga <jan.varga@gmail.com>
Mon, 29 Apr 2019 06:05:55 +0200
changeset 531555 2cf5f9810f54b12e99f776f4fa73d1a5a87f7d27
parent 531554 4294ffae23f6f3320e61690f8feb610c9b51e37a
child 531556 254a8c04cb3a1cf44df267b9a42f223c0e226aad
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1547452
milestone68.0a1
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 1547452 - LSNG: Refactor Datastore::GetSnapshotInitInfo using a switch statement; r=asuth Differential Revision: https://phabricator.services.mozilla.com/D29137
dom/localstorage/ActorsParent.cpp
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -4659,56 +4659,89 @@ void Datastore::GetSnapshotInitInfo(cons
                                     uint32_t& aNextLoadIndex,
                                     uint32_t& aTotalLength,
                                     int64_t& aInitialUsage, int64_t& aPeakUsage,
                                     LSSnapshot::LoadState& aLoadState) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mClosed);
   MOZ_ASSERT(!mInUpdateBatch);
 
-  nsString value;
-  int64_t sizeOfKey = 0;
-  int64_t sizeOfItem = 0;
-  bool checkKey = false;
-
-  if (!aKey.IsVoid()) {
-    GetItem(aKey, value);
-    if (!value.IsVoid()) {
-      sizeOfKey = aKey.Length();
-      sizeOfItem = sizeOfKey + value.Length();
-      checkKey = true;
-    }
-  }
-
 #ifdef DEBUG
   int64_t sizeOfKeys = 0;
   int64_t sizeOfItems = 0;
   for (auto item : mOrderedItems) {
     int64_t sizeOfKey = static_cast<int64_t>(item.key().Length());
     sizeOfKeys += sizeOfKey;
     sizeOfItems += sizeOfKey + static_cast<int64_t>(item.value().Length());
   }
   MOZ_ASSERT(mSizeOfKeys == sizeOfKeys);
   MOZ_ASSERT(mSizeOfItems == sizeOfItems);
 #endif
 
-  if (mSizeOfKeys - sizeOfKey <= gSnapshotPrefill) {
-    if (mSizeOfItems - sizeOfItem <= gSnapshotPrefill) {
+  // Computes load state optimized for current size of keys and items.
+  // Zero key length and value can be passed to do a quick initial estimation.
+  // If computed load state is already AllOrderedItems then excluded key length
+  // and value length can't make it any better.
+  auto GetLoadState = [&](auto aKeyLength, auto aValueLength) {
+    if (mSizeOfKeys - aKeyLength <= gSnapshotPrefill) {
+      if (mSizeOfItems - aKeyLength - aValueLength <= gSnapshotPrefill) {
+        return LSSnapshot::LoadState::AllOrderedItems;
+      }
+
+      return LSSnapshot::LoadState::AllOrderedKeys;
+    }
+
+    return LSSnapshot::LoadState::Partial;
+  };
+
+  // Value for given aKey if aKey is not void (can be void too if value doesn't
+  // exist for given aKey).
+  nsString value;
+  // If aKey and value are not void, checkKey will be set to true. Once we find
+  // an item for given aKey in one of the loops below, checkKey is set to false
+  // to prevent additional comparison of strings (string implementation compares
+  // string lengths first to avoid char by char comparison if possible).
+  bool checkKey = false;
+
+  // Avoid additional hash lookup if all ordered items fit into initial prefill
+  // already.
+  LSSnapshot::LoadState loadState = GetLoadState(/* aKeyLength */ 0,
+                                                 /* aValueLength */ 0);
+  if (loadState != LSSnapshot::LoadState::AllOrderedItems && !aKey.IsVoid()) {
+    GetItem(aKey, value);
+    if (!value.IsVoid()) {
+      // Ok, we have a non void aKey and value.
+
+      // We have to watch for aKey during one of the loops below to exclude it
+      // from the size computation. The super fast mode (AllOrderedItems)
+      // doesn't have to do that though.
+      checkKey = true;
+
+      // We have to compute load state again because aKey length and value
+      // length is excluded from the size in this case.
+      loadState = GetLoadState(aKey.Length(), value.Length());
+    }
+  }
+
+  switch (loadState) {
+    case LSSnapshot::LoadState::AllOrderedItems: {
       // We're sending all ordered items, we don't need to check keys because
       // mOrderedItems must contain a value for aKey if checkKey is true.
 
       aItemInfos.AppendElements(mOrderedItems);
 
       MOZ_ASSERT(aItemInfos.Length() == mValues.Count());
       aNextLoadIndex = mValues.Count();
 
       aAddKeyToUnknownItems = false;
 
-      aLoadState = LSSnapshot::LoadState::AllOrderedItems;
-    } else {
+      break;
+    }
+
+    case LSSnapshot::LoadState::AllOrderedKeys: {
       // We don't have enough snapshot budget to send all items, but we do have
       // enough to send all of the keys and to make a best effort to populate as
       // many values as possible. We send void string values once we run out of
       // budget. A complicating factor is that we want to make sure that we send
       // the value for aKey which is a localStorage read that's triggering this
       // request. Since that key can happen anywhere in the list of items, we
       // need to handle it specially.
       //
@@ -4760,65 +4793,75 @@ void Datastore::GetSnapshotInitInfo(cons
         } else {
           aLoadedItems.PutEntry(key);
           itemInfo->value() = value;
         }
       }
 
       aAddKeyToUnknownItems = false;
 
-      aLoadState = LSSnapshot::LoadState::AllOrderedKeys;
-    }
-  } else {
-    int64_t size = 0;
-    for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
-      const LSItemInfo& item = mOrderedItems[index];
-
-      const nsString& key = item.key();
-      const nsString& value = item.value();
-
-      if (checkKey && key == aKey) {
-        checkKey = false;
-      } else {
-        size += static_cast<int64_t>(key.Length()) +
-                static_cast<int64_t>(value.Length());
-
-        if (size > gSnapshotPrefill) {
-          aNextLoadIndex = index;
-          break;
+      break;
+    }
+
+    case LSSnapshot::LoadState::Partial: {
+      int64_t size = 0;
+      for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
+        const LSItemInfo& item = mOrderedItems[index];
+
+        const nsString& key = item.key();
+        const nsString& value = item.value();
+
+        if (checkKey && key == aKey) {
+          checkKey = false;
+        } else {
+          size += static_cast<int64_t>(key.Length()) +
+                  static_cast<int64_t>(value.Length());
+
+          if (size > gSnapshotPrefill) {
+            aNextLoadIndex = index;
+            break;
+          }
+        }
+
+        aLoadedItems.PutEntry(key);
+
+        LSItemInfo* itemInfo = aItemInfos.AppendElement();
+        itemInfo->key() = key;
+        itemInfo->value() = value;
+      }
+
+      aAddKeyToUnknownItems = false;
+
+      if (!aKey.IsVoid()) {
+        if (value.IsVoid()) {
+          aAddKeyToUnknownItems = true;
+        } else if (checkKey) {
+          // The item wasn't added in the loop above, add it here.
+
+          LSItemInfo* itemInfo = aItemInfos.AppendElement();
+          itemInfo->key() = aKey;
+          itemInfo->value() = value;
         }
       }
 
-      aLoadedItems.PutEntry(key);
-
-      LSItemInfo* itemInfo = aItemInfos.AppendElement();
-      itemInfo->key() = key;
-      itemInfo->value() = value;
-    }
-
-    aAddKeyToUnknownItems = false;
-
-    if (!aKey.IsVoid()) {
-      if (value.IsVoid()) {
-        aAddKeyToUnknownItems = true;
-      } else if (checkKey) {
-        LSItemInfo* itemInfo = aItemInfos.AppendElement();
-        itemInfo->key() = aKey;
-        itemInfo->value() = value;
-      }
-    }
-
-    MOZ_ASSERT(aItemInfos.Length() < mOrderedItems.Length());
-    aLoadState = LSSnapshot::LoadState::Partial;
+      MOZ_ASSERT(aItemInfos.Length() < mOrderedItems.Length());
+
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Bad load state value!");
   }
 
   aTotalLength = mValues.Count();
 
   aInitialUsage = mUsage;
   aPeakUsage = aInitialUsage;
+
+  aLoadState = loadState;
 }
 
 void Datastore::GetItem(const nsString& aKey, nsString& aValue) const {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mClosed);
 
   if (!mValues.Get(aKey, &aValue)) {
     aValue.SetIsVoid(true);