Bug 1262826 - specialize nsTHashtable<nsPtrHashKey<T>> to share a common implementation; r=erahm

We have a number of nsTHashtable<nsPtrHashKey<T>> instantiations, mostly
from IPDL-generated code.  There's no reason in principle that all of
these instantiations couldn't share code, since they're all storing POD
entries of the same size.  Let's specialize nsTHashtable for such types,
providing a thin layer over a hashtable that stores void pointers.  This
change saves about 90K of space (!) on x86-64 Linux.
This commit is contained in:
Nathan Froyd 2016-08-22 18:45:47 -04:00
parent 41ba39b3b0
commit e255fd3842
4 changed files with 189 additions and 31 deletions

View File

@ -44,6 +44,7 @@ EXPORTS += [
'nsIWeakReferenceUtils.h',
'nsJSThingHashtable.h',
'nsMemory.h',
'nsPointerHashKeys.h',
'nsProxyRelease.h',
'nsQuickSort.h',
'nsRefPtrHashtable.h',

View File

@ -18,6 +18,7 @@
#include "nsStringGlue.h"
#include "nsCRTGlue.h"
#include "nsUnicharUtils.h"
#include "nsPointerHashKeys.h"
#include <stdlib.h>
#include <string.h>
@ -358,36 +359,6 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
CycleCollectionNoteChild(aCallback, aField.GetKey(), aName, aFlags);
}
/**
* hashkey wrapper using T* KeyType
*
* @see nsTHashtable::EntryType for specification
*/
template<class T>
class nsPtrHashKey : public PLDHashEntryHdr
{
public:
typedef T* KeyType;
typedef const T* KeyTypePointer;
explicit nsPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
nsPtrHashKey(const nsPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
~nsPtrHashKey() {}
KeyType GetKey() const { return mKey; }
bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
return NS_PTR_TO_UINT32(aKey) >> 2;
}
enum { ALLOW_MEMMOVE = true };
protected:
T* MOZ_NON_OWNING_REF mKey;
};
/**
* hashkey wrapper using T* KeyType that sets key to nullptr upon
* destruction. Relevant only in cases where a memory pointer-scanner
@ -408,7 +379,6 @@ public:
~nsClearingPtrHashKey() { nsPtrHashKey<T>::mKey = nullptr; }
};
typedef nsPtrHashKey<const void> nsVoidPtrHashKey;
typedef nsClearingPtrHashKey<const void> nsClearingVoidPtrHashKey;
/**

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Definitions for nsPtrHashKey<T> and nsVoidPtrHashKey. */
#ifndef nsPointerHashKeys_h
#define nsPointerHashKeys_h
#include "nscore.h"
#include "mozilla/Attributes.h"
/**
* hashkey wrapper using T* KeyType
*
* @see nsTHashtable::EntryType for specification
*/
template<class T>
class nsPtrHashKey : public PLDHashEntryHdr
{
public:
typedef T* KeyType;
typedef const T* KeyTypePointer;
explicit nsPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
nsPtrHashKey(const nsPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
~nsPtrHashKey() {}
KeyType GetKey() const { return mKey; }
bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
return NS_PTR_TO_UINT32(aKey) >> 2;
}
enum { ALLOW_MEMMOVE = true };
protected:
T* MOZ_NON_OWNING_REF mKey;
};
typedef nsPtrHashKey<const void> nsVoidPtrHashKey;
#endif // nsPointerHashKeys_h

View File

@ -8,6 +8,7 @@
#define nsTHashtable_h__
#include "PLDHashTable.h"
#include "nsPointerHashKeys.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/fallible.h"
@ -435,4 +436,142 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
}
}
/**
* For nsTHashtable with pointer entries, we can have a template specialization
* that layers a typed T* interface on top of a common implementation that
* works internally with void pointers. This arrangement saves code size and
* might slightly improve performance as well.
*/
/**
* We need a separate entry type class for the inheritance structure of the
* nsTHashtable specialization below; nsVoidPtrHashKey is simply typedefed to a
* specialization of nsPtrHashKey, and the formulation:
*
* class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<nsPtrHashKey<const void>
*
* is not going to turn out very well, since we'd wind up with an nsTHashtable
* instantiation that is its own base class.
*/
namespace detail {
class VoidPtrHashKey : public nsPtrHashKey<const void>
{
typedef nsPtrHashKey<const void> Base;
public:
explicit VoidPtrHashKey(const void* aKey) : Base(aKey) {}
};
} // namespace detail
/**
* See the main nsTHashtable documentation for descriptions of this class's
* methods.
*/
template<typename T>
class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<::detail::VoidPtrHashKey>
{
typedef nsTHashtable<::detail::VoidPtrHashKey> Base;
typedef nsPtrHashKey<T> EntryType;
// We play games with reinterpret_cast'ing between these two classes, so
// try to ensure that playing said games is reasonable.
static_assert(sizeof(nsPtrHashKey<T>) == sizeof(::detail::VoidPtrHashKey),
"hash keys must be the same size");
nsTHashtable(const nsTHashtable& aOther) = delete;
nsTHashtable& operator=(const nsTHashtable& aOther) = delete;
public:
nsTHashtable() = default;
explicit nsTHashtable(uint32_t aInitLength)
: Base(aInitLength)
{}
~nsTHashtable() = default;
nsTHashtable(nsTHashtable&&) = default;
/* Wrapper functions */
using Base::GetGeneration;
using Base::Count;
using Base::IsEmpty;
using Base::Clear;
using Base::ShallowSizeOfExcludingThis;
using Base::ShallowSizeOfIncludingThis;
#ifdef DEBUG
using Base::MarkImmutable;
#endif
EntryType* GetEntry(T* aKey) const
{
return reinterpret_cast<EntryType*>(Base::GetEntry(aKey));
}
bool Contains(T* aKey) const
{
return Base::Contains(aKey);
}
EntryType* PutEntry(T* aKey)
{
return reinterpret_cast<EntryType*>(Base::PutEntry(aKey));
}
MOZ_MUST_USE
EntryType* PutEntry(T* aKey, const mozilla::fallible_t&)
{
return reinterpret_cast<EntryType*>(
Base::PutEntry(aKey, mozilla::fallible));
}
void RemoveEntry(T* aKey)
{
Base::RemoveEntry(aKey);
}
void RemoveEntry(EntryType* aEntry)
{
Base::RemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
}
void RawRemoveEntry(EntryType* aEntry)
{
Base::RawRemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
}
class Iterator : public Base::Iterator
{
public:
typedef nsTHashtable::Base::Iterator Base;
explicit Iterator(nsTHashtable* aTable) : Base(aTable) {}
Iterator(Iterator&& aOther) : Base(mozilla::Move(aOther)) {}
~Iterator() = default;
EntryType* Get() const { return reinterpret_cast<EntryType*>(Base::Get()); }
private:
Iterator() = delete;
Iterator(const Iterator&) = delete;
Iterator& operator=(const Iterator&) = delete;
Iterator& operator=(Iterator&&) = delete;
};
Iterator Iter() { return Iterator(this); }
Iterator ConstIter() const
{
return Iterator(const_cast<nsTHashtable*>(this));
}
void SwapElements(nsTHashtable& aOther)
{
Base::SwapElements(aOther);
}
};
#endif // nsTHashtable_h__