HphpArray compact layout
ArrayData gets on a diet, down to 32 bytes devirtualized destructor call in HphpArray::release() aggressively inlined constructors for ArrayData (do away with defaulted arguments) eliminate a dead memset() for the hashtable in HphpArray(uint size, const TypedValue* values)
Esse commit está contido em:
commit de
Sara Golemon
pai
5f75fbcf69
commit
6d365df98f
@@ -360,7 +360,7 @@ Variant ArrayData::key() const {
|
||||
return uninit_null();
|
||||
}
|
||||
|
||||
Variant ArrayData::value(ssize_t &pos) const {
|
||||
Variant ArrayData::value(int32_t &pos) const {
|
||||
if (size_t(pos) < size_t(size())) {
|
||||
return getValue(pos);
|
||||
}
|
||||
|
||||
@@ -55,14 +55,35 @@ class ArrayData : public Countable {
|
||||
public:
|
||||
static const ssize_t invalid_index = -1;
|
||||
|
||||
explicit ArrayData(ArrayKind kind, bool nonsmart = false) :
|
||||
m_size(-1), m_pos(0), m_strongIterators(0), m_kind(kind),
|
||||
m_nonsmart(nonsmart) {
|
||||
explicit ArrayData(ArrayKind kind)
|
||||
: m_size(-1)
|
||||
, m_pos(0)
|
||||
, m_kind(kind)
|
||||
, m_nonsmart(false)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
ArrayData(const ArrayData *src, ArrayKind kind,
|
||||
bool nonsmart = false) :
|
||||
m_pos(src->m_pos), m_strongIterators(0), m_kind(src->m_kind),
|
||||
m_nonsmart(nonsmart) {
|
||||
|
||||
ArrayData(ArrayKind kind, bool nonsmart)
|
||||
: m_size(-1)
|
||||
, m_pos(0)
|
||||
, m_kind(kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
|
||||
ArrayData(ArrayKind kind, bool nonsmart, uint size)
|
||||
: m_size(size)
|
||||
, m_pos(size ? 0 : ArrayData::invalid_index)
|
||||
, m_kind(kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
|
||||
ArrayData(const ArrayData *src, ArrayKind kind, bool nonsmart = false)
|
||||
: m_pos(src->m_pos)
|
||||
, m_kind(src->m_kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
|
||||
static HphpArray* Make(uint capacity);
|
||||
@@ -182,7 +203,7 @@ class ArrayData : public Countable {
|
||||
virtual Variant next();
|
||||
virtual Variant end();
|
||||
virtual Variant key() const;
|
||||
virtual Variant value(ssize_t &pos) const;
|
||||
virtual Variant value(int32_t &pos) const;
|
||||
virtual Variant each();
|
||||
|
||||
bool isHead() const { return m_pos == iter_begin(); }
|
||||
@@ -479,16 +500,17 @@ class ArrayData : public Countable {
|
||||
static bool IsValidKey(const StringData* k) { return k; }
|
||||
|
||||
protected:
|
||||
// Layout starts with 64 bits for vtable, then 32 bits for m_count
|
||||
// from Countable base, then...
|
||||
uint m_size;
|
||||
ssize_t m_pos;
|
||||
private:
|
||||
FullPos* m_strongIterators; // head of linked list
|
||||
int32_t m_pos;
|
||||
protected:
|
||||
const ArrayKind m_kind;
|
||||
AllocMode m_allocMode;
|
||||
const bool m_nonsmart; // never use smartalloc to allocate Elms
|
||||
/* The 4 bytes of padding here are available to subclasses if their
|
||||
* first field is also <= 4 bytes. */
|
||||
uint8_t m_slack8; // use this for whatever you wanna
|
||||
private:
|
||||
FullPos* m_strongIterators; // head of linked list
|
||||
|
||||
public: // for the JIT
|
||||
static uint32_t getKindOff() {
|
||||
|
||||
@@ -66,7 +66,13 @@ static const Trace::Module TRACEMOD = Trace::runtime;
|
||||
* separately. Even larger tables allocate the hashtable and slots
|
||||
* contiguously.
|
||||
*/
|
||||
IMPLEMENT_SMART_ALLOCATION(HphpArray);
|
||||
void *HphpArray::SmaAllocatorInitSetup = SmartAllocatorInitSetup<HphpArray>();
|
||||
|
||||
void HphpArray::release() {
|
||||
assert(typeid(*this) == typeid(HphpArray));
|
||||
this->HphpArray::~HphpArray();
|
||||
HphpArray::AllocatorType::getNoCheck()->dealloc(this);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Static members.
|
||||
@@ -76,12 +82,12 @@ HphpArray HphpArray::s_theEmptyArray(StaticEmptyArray);
|
||||
//=============================================================================
|
||||
// Helpers.
|
||||
|
||||
static inline size_t computeMaskFromNumElms(uint32_t numElms) {
|
||||
assert(numElms <= 0x7fffffffU);
|
||||
static inline size_t computeMaskFromNumElms(const uint32_t n) {
|
||||
assert(n <= 0x7fffffffU);
|
||||
size_t lgSize = HphpArray::MinLgTableSize;
|
||||
size_t maxElms = (size_t(3U)) << (lgSize-2);
|
||||
size_t maxElms = (size_t(3U)) << (HphpArray::MinLgTableSize - 2);
|
||||
assert(lgSize >= 2);
|
||||
while (maxElms < numElms) {
|
||||
while (maxElms < n) {
|
||||
++lgSize;
|
||||
maxElms <<= 1;
|
||||
}
|
||||
@@ -95,60 +101,72 @@ static inline size_t computeMaskFromNumElms(uint32_t numElms) {
|
||||
//=============================================================================
|
||||
// Construction/destruction.
|
||||
|
||||
inline void HphpArray::init(uint size) {
|
||||
m_size = 0;
|
||||
m_tableMask = computeMaskFromNumElms(size);
|
||||
size_t tableSize = computeTableSize(m_tableMask);
|
||||
size_t maxElms = computeMaxElms(m_tableMask);
|
||||
allocData(maxElms, tableSize);
|
||||
inline uint32_t HphpArray::initWithoutHash(uint capacity) {
|
||||
m_tableMask = computeMaskFromNumElms(capacity);
|
||||
auto const tableSize = computeTableSize(m_tableMask);
|
||||
allocData(computeMaxElms(m_tableMask), tableSize);
|
||||
return tableSize;
|
||||
}
|
||||
|
||||
inline void HphpArray::init(uint capacity) {
|
||||
assert(m_size == 0);
|
||||
const auto tableSize = initWithoutHash(capacity);
|
||||
initHash(m_hash, tableSize);
|
||||
m_pos = ArrayData::invalid_index;
|
||||
}
|
||||
|
||||
HphpArray::HphpArray(uint size) : ArrayData(kHphpArray),
|
||||
m_lastE(ElmIndEmpty), m_data(nullptr), m_nextKI(0), m_hLoad(0) {
|
||||
HphpArray::HphpArray(uint size) : ArrayData(kHphpArray, false, 0),
|
||||
m_lastE(ElmIndEmpty), m_hLoad(0), m_nextKI(0) {
|
||||
#ifdef PEDANTIC
|
||||
if (size > 0x7fffffffU) {
|
||||
raise_error("Cannot create an array with more than 2^31 - 1 elements");
|
||||
}
|
||||
#endif
|
||||
assert(m_size == 0);
|
||||
init(size);
|
||||
}
|
||||
|
||||
HphpArray::HphpArray(uint size, const TypedValue* values) :
|
||||
ArrayData(kHphpArray), m_lastE(ElmIndEmpty), m_data(nullptr),
|
||||
m_nextKI(0), m_hLoad(0)
|
||||
{
|
||||
HphpArray::HphpArray(uint size, const TypedValue* values)
|
||||
: ArrayData(kHphpArray, false, size)
|
||||
, m_lastE(size - 1)
|
||||
, m_hLoad(size)
|
||||
, m_nextKI(size) {
|
||||
#ifdef PEDANTIC
|
||||
if (size > 0x7fffffffU) {
|
||||
raise_error("Cannot create an array with more than 2^31 - 1 elements");
|
||||
}
|
||||
#endif
|
||||
init(size);
|
||||
initWithoutHash(size);
|
||||
assert(size <= m_tableMask + 1);
|
||||
// append values by moving -- Caller assumes we update refcount. Values
|
||||
// are in reverse order since they come from the stack, which grows down.
|
||||
// This code is hand-specialized from nextInsert().
|
||||
assert(m_size == 0 && m_hLoad == 0 && m_nextKI == 0);
|
||||
assert(m_size == size && m_hLoad == size && m_nextKI == size);
|
||||
ElmInd* hash = m_hash;
|
||||
Elm* data = m_data;
|
||||
for (uint i = 0; i < size; i++) {
|
||||
assert(hash[i] == HphpArray::ElmIndEmpty);
|
||||
uint i = 0;
|
||||
for (; i < size; i++) {
|
||||
const TypedValue& tv = values[size - i - 1];
|
||||
data[i].data.m_data = tv.m_data;
|
||||
data[i].data.m_type = tv.m_type;
|
||||
data[i].setIntKey(i);
|
||||
hash[i] = i;
|
||||
}
|
||||
m_size = size;
|
||||
m_hLoad = size;
|
||||
m_lastE = size - 1;
|
||||
m_nextKI = size;
|
||||
if (size > 0) m_pos = 0;
|
||||
// Initialize the leftover hash
|
||||
for (; i <= m_tableMask; i++) {
|
||||
hash[i] = ElmIndEmpty;
|
||||
}
|
||||
assert(m_size == size);
|
||||
assert(m_hLoad == size);
|
||||
assert(m_lastE == size - 1);
|
||||
assert(m_nextKI == size);
|
||||
assert(size == 0 || m_pos == 0);
|
||||
}
|
||||
|
||||
HphpArray::HphpArray(EmptyMode) : ArrayData(kHphpArray),
|
||||
m_lastE(ElmIndEmpty), m_data(nullptr), m_nextKI(0), m_hLoad(0) {
|
||||
HphpArray::HphpArray(EmptyMode)
|
||||
: ArrayData(kHphpArray, false, 0)
|
||||
, m_lastE(ElmIndEmpty)
|
||||
, m_hLoad(0)
|
||||
, m_nextKI(0) {
|
||||
init(0);
|
||||
setStatic();
|
||||
}
|
||||
@@ -189,10 +207,10 @@ void HphpArray::dumpDebugInfo() const {
|
||||
"--- dumpDebugInfo(this=0x%08zx) ----------------------------\n",
|
||||
uintptr_t(this));
|
||||
fprintf(stderr, "m_data = %p\tm_hash = %p\n"
|
||||
"m_tableMask = %u\tm_size = %d\tm_hLoad = %d\n"
|
||||
"m_nextKI = %" PRId64 "\t\tm_lastE = %d\tm_pos = %zd\n",
|
||||
m_data, m_hash, m_tableMask, m_size, m_hLoad,
|
||||
m_nextKI, m_lastE, m_pos);
|
||||
"m_tableMask = %u\tm_size = %d\tm_hLoad = %d\n"
|
||||
"m_nextKI = %" PRId64 "\t\tm_lastE = %d\tm_pos = %zd\n",
|
||||
m_data, m_hash, m_tableMask, m_size, m_hLoad,
|
||||
m_nextKI, m_lastE, size_t(m_pos));
|
||||
fprintf(stderr, "Elements:\n");
|
||||
ssize_t lastE = m_lastE;
|
||||
Elm* elms = m_data;
|
||||
@@ -394,7 +412,7 @@ Variant HphpArray::key() const {
|
||||
return uninit_null();
|
||||
}
|
||||
|
||||
Variant HphpArray::value(ssize_t& pos) const {
|
||||
Variant HphpArray::value(int32_t& pos) const {
|
||||
if (pos != ArrayData::invalid_index) {
|
||||
Elm* e = &m_data[pos];
|
||||
assert(e->data.m_type != KindOfTombstone);
|
||||
@@ -703,7 +721,6 @@ void HphpArray::newElmStr(ElmInd* ei, strhash_t h, StringData* key,
|
||||
}
|
||||
|
||||
void HphpArray::allocData(size_t maxElms, size_t tableSize) {
|
||||
assert(!m_data);
|
||||
if (maxElms <= SmallSize) {
|
||||
m_data = m_inline_data.slots;
|
||||
m_hash = m_inline_data.hash;
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
Variant next();
|
||||
Variant end();
|
||||
Variant key() const;
|
||||
Variant value(ssize_t& pos) const;
|
||||
Variant value(int32_t& pos) const;
|
||||
Variant each();
|
||||
|
||||
// implements ArrayData
|
||||
@@ -297,6 +297,7 @@ public:
|
||||
// Use a minimum of an 4-element hash table. Valid range: [2..32]
|
||||
static const uint32_t MinLgTableSize = 2;
|
||||
static const uint32_t SmallHashSize = 1 << MinLgTableSize;
|
||||
static const uint32_t SmallMask = SmallHashSize - 1;
|
||||
static const uint32_t SmallSize = SmallHashSize - SmallHashSize / LoadScale;
|
||||
|
||||
struct InlineSlots {
|
||||
@@ -358,11 +359,11 @@ private:
|
||||
// +--------------------+
|
||||
|
||||
ElmInd m_lastE; // Index of last used element.
|
||||
uint32_t m_tableMask; // Bitmask used when indexing into the hash table.
|
||||
uint32_t m_hLoad; // Hash table load (# of non-empty slots).
|
||||
int64_t m_nextKI; // Next integer key to use for append.
|
||||
Elm* m_data; // Contains elements and hash table.
|
||||
ElmInd* m_hash; // Hash table.
|
||||
int64_t m_nextKI; // Next integer key to use for append.
|
||||
uint32_t m_tableMask; // Bitmask used when indexing into the hash table.
|
||||
uint32_t m_hLoad; // Hash table load (# of non-empty slots).
|
||||
union {
|
||||
InlineSlots m_inline_data;
|
||||
ElmInd m_inline_hash[sizeof(m_inline_data) / sizeof(ElmInd)];
|
||||
@@ -439,9 +440,11 @@ private:
|
||||
void reallocData(size_t maxElms, size_t tableSize, uint oldMask);
|
||||
|
||||
/**
|
||||
* init(size) allocates space for size elements but initializes
|
||||
* as an empty array
|
||||
* init(size) allocates space for size elements but initializes as
|
||||
* an empty array. The "WithoutHash" version does not initialize the
|
||||
* hash table and returns the hash table size.
|
||||
*/
|
||||
uint32_t initWithoutHash(uint size);
|
||||
void init(uint size);
|
||||
|
||||
/**
|
||||
|
||||
@@ -284,7 +284,7 @@ Variant ArrayShell::key() const {
|
||||
return key(toPos(m_pos));
|
||||
}
|
||||
|
||||
Variant ArrayShell::value(ssize_t &pos) const {
|
||||
Variant ArrayShell::value(int32_t &pos) const {
|
||||
NOT_IMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ static uint32_t toUint32(PosType pos) {
|
||||
}
|
||||
|
||||
// Signed to pos, no change in representation.
|
||||
inline PosType toPos(int32_t n) {
|
||||
static_assert(sizeof(int64_t) == sizeof(PosType), "");
|
||||
return static_cast<PosType>(n);
|
||||
}
|
||||
static PosType toPos(int64_t n) {
|
||||
static_assert(sizeof(int64_t) == sizeof(PosType), "can't");
|
||||
return static_cast<PosType>(n);
|
||||
@@ -379,7 +383,7 @@ public:
|
||||
virtual Variant next() FOLLY_OVERRIDE;
|
||||
virtual Variant end() FOLLY_OVERRIDE;
|
||||
virtual Variant key() const FOLLY_OVERRIDE;
|
||||
virtual Variant value(ssize_t &pos) const FOLLY_OVERRIDE;
|
||||
virtual Variant value(int32_t &pos) const FOLLY_OVERRIDE;
|
||||
virtual Variant each() FOLLY_OVERRIDE;
|
||||
|
||||
virtual bool isInvalid() const FOLLY_OVERRIDE {
|
||||
|
||||
@@ -252,7 +252,7 @@ Variant NameValueTableWrapper::key() const {
|
||||
return null_variant;
|
||||
}
|
||||
|
||||
Variant NameValueTableWrapper::value(ssize_t& pos) const {
|
||||
Variant NameValueTableWrapper::value(int32_t& pos) const {
|
||||
return current();
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ public: // ArrayData implementation
|
||||
virtual Variant end();
|
||||
virtual CVarRef endRef();
|
||||
virtual Variant key() const;
|
||||
virtual Variant value(ssize_t &pos) const;
|
||||
virtual Variant value(int32_t &pos) const;
|
||||
virtual Variant each();
|
||||
virtual bool validFullPos(const FullPos & fp) const;
|
||||
virtual bool advanceFullPos(FullPos&);
|
||||
@@ -149,7 +149,7 @@ private:
|
||||
|
||||
class GlobalNameValueTableWrapper : public NameValueTableWrapper {
|
||||
public:
|
||||
GlobalNameValueTableWrapper(NameValueTable* tab);
|
||||
explicit GlobalNameValueTableWrapper(NameValueTable* tab);
|
||||
Variant gvm_argc;
|
||||
Variant gvm_argv;
|
||||
Variant gvm__SERVER;
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário