Bug 1441754 - Shrink CallbackNode by combining mMatchKind and mNext into a tagged pointer. r=glandium draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 28 Feb 2018 16:21:42 +1100
changeset 760834 f56b4aab9dd31ab3f04459f1f134085df29a6fa4
parent 760833 e5d911a69697461aa91914cd08c02a97ad625f4c
child 760835 cd33f4116beb70d21493b6c31e5e2aff76bc35d4
push id100761
push usernnethercote@mozilla.com
push dateWed, 28 Feb 2018 07:48:23 +0000
reviewersglandium
bugs1441754
milestone60.0a1
Bug 1441754 - Shrink CallbackNode by combining mMatchKind and mNext into a tagged pointer. r=glandium The following table shows the effect of this change: > old 64-bit new 64-bit old 32-bit new 32-bit > sizeof(CallbackNode) 40 32 20 16 > size when heap allocated 48 32 32 16 This reduces memory usage by about 15--40 KB per process. MozReview-Commit-ID: 4gXgGI3SiJz
modules/libpref/Preferences.cpp
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -751,51 +751,71 @@ class CallbackNode
 public:
   CallbackNode(const char* aDomain,
                PrefChangedFunc aFunc,
                void* aData,
                Preferences::MatchKind aMatchKind)
     : mDomain(moz_xstrdup(aDomain))
     , mFunc(aFunc)
     , mData(aData)
-    , mMatchKind(aMatchKind)
-    , mNext(nullptr)
+    , mNextAndMatchKind(aMatchKind)
   {
   }
 
   // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
   // borrows.
   const char* Domain() const { return mDomain.get(); }
 
   PrefChangedFunc Func() const { return mFunc; }
   void ClearFunc() { mFunc = nullptr; }
 
   void* Data() const { return mData; }
 
-  Preferences::MatchKind MatchKind() const { return mMatchKind; }
-
-  CallbackNode* Next() const { return mNext; }
-  void SetNext(CallbackNode* aNext) { mNext = aNext; }
+  Preferences::MatchKind MatchKind() const
+  {
+    return static_cast<Preferences::MatchKind>(mNextAndMatchKind &
+                                               kMatchKindMask);
+  }
+
+  CallbackNode* Next() const
+  {
+    return reinterpret_cast<CallbackNode*>(mNextAndMatchKind & kNextMask);
+  }
+
+  void SetNext(CallbackNode* aNext)
+  {
+    uintptr_t matchKind = mNextAndMatchKind & kMatchKindMask;
+    mNextAndMatchKind = reinterpret_cast<uintptr_t>(aNext);
+    MOZ_ASSERT(mNextAndMatchKind & kMatchKindMask == 0);
+    mNextAndMatchKind |= matchKind;
+  }
 
   void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
   {
     aSizes.mCallbacksObjects += aMallocSizeOf(this);
     aSizes.mCallbacksDomains += aMallocSizeOf(mDomain.get());
   }
 
 private:
+  static const uintptr_t kMatchKindMask = uintptr_t(0x1);
+  static const uintptr_t kNextMask = ~kMatchKindMask;
+
   UniqueFreePtr<const char> mDomain;
 
   // If someone attempts to remove the node from the callback list while
   // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
   // be removed at the end of NotifyCallbacks().
   PrefChangedFunc mFunc;
   void* mData;
-  Preferences::MatchKind mMatchKind;
-  CallbackNode* mNext;
+
+  // Conceptually this is two fields:
+  // - CallbackNode* mNext;
+  // - Preferences::MatchKind mMatchKind;
+  // They are combined into a tagged pointer to save memory.
+  uintptr_t mNextAndMatchKind;
 };
 
 static PLDHashTable* gHashTable;
 
 // The callback list contains all the priority callbacks followed by the
 // non-priority callbacks. gLastPriorityNode records where the first part ends.
 static CallbackNode* gFirstCallback = nullptr;
 static CallbackNode* gLastPriorityNode = nullptr;