HphpArray
I've run a bunch of experiments on HphpArray speed and am still yet to have a major breakthrough. However, there are a few clear small winners that I'm submitting with this diff. CPU instructions, CPU loads, and CPU stores are all as green as the Eternal Hunting Grounds. Speed is drowned in noise but I sususpect will be measurable on these changes combined.
Esse commit está contido em:
commit de
Sara Golemon
pai
110f05277c
commit
872d24b761
@@ -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,26 @@ 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, bool nonsmart = false)
|
||||
: m_size(-1)
|
||||
, m_kind(kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_pos(0)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
explicit ArrayData(ArrayKind kind, bool nonsmart, uint size)
|
||||
: m_size(size)
|
||||
, m_kind(kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_pos(size ? 0 : ArrayData::invalid_index)
|
||||
, 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) {
|
||||
bool nonsmart = false)
|
||||
: m_kind(src->m_kind)
|
||||
, m_nonsmart(nonsmart)
|
||||
, m_pos(src->m_pos)
|
||||
, m_strongIterators(nullptr) {
|
||||
}
|
||||
|
||||
static HphpArray* Make(uint capacity);
|
||||
@@ -184,7 +196,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(); }
|
||||
@@ -482,13 +494,13 @@ class ArrayData : public Countable {
|
||||
|
||||
protected:
|
||||
uint m_size;
|
||||
ssize_t m_pos;
|
||||
private:
|
||||
FullPos* m_strongIterators; // head of linked list
|
||||
protected:
|
||||
const ArrayKind m_kind;
|
||||
AllocMode m_allocMode;
|
||||
const bool m_nonsmart; // never use smartalloc to allocate Elms
|
||||
int32_t m_pos;
|
||||
private:
|
||||
FullPos* m_strongIterators; // head of linked list
|
||||
protected:
|
||||
/* The 4 bytes of padding here are available to subclasses if their
|
||||
* first field is also <= 4 bytes. */
|
||||
|
||||
|
||||
@@ -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,55 +82,57 @@ HphpArray HphpArray::s_theEmptyArray(StaticEmptyArray);
|
||||
//=============================================================================
|
||||
// Helpers.
|
||||
|
||||
static inline size_t computeMaskFromNumElms(uint32_t numElms) {
|
||||
assert(numElms <= 0x7fffffffU);
|
||||
size_t lgSize = HphpArray::MinLgTableSize;
|
||||
size_t maxElms = (size_t(3U)) << (lgSize-2);
|
||||
assert(lgSize >= 2);
|
||||
while (maxElms < numElms) {
|
||||
++lgSize;
|
||||
maxElms <<= 1;
|
||||
static inline size_t computeMaskFromNumElms(const uint32_t n) {
|
||||
if (n <= HphpArray::SmallSize) {
|
||||
return HphpArray::SmallMask;
|
||||
}
|
||||
assert(lgSize <= 32);
|
||||
// return 2^lgSize - 1
|
||||
return ((size_t(1U)) << lgSize) - 1;
|
||||
static_assert(HphpArray::MinLgTableSize >= 2,
|
||||
"lower limit for 0.75 load factor");
|
||||
uint32_t cap = (3U << (HphpArray::MinLgTableSize - 2)) * 2;
|
||||
while (cap < n) {
|
||||
cap *= 2;
|
||||
}
|
||||
return ((size_t(cap) << 2UL) & (size_t(cap) << 1UL)) - 1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// 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) {
|
||||
assert(m_size == 0);
|
||||
m_tableMask = computeMaskFromNumElms(capacity);
|
||||
auto const tableSize = computeTableSize(m_tableMask);
|
||||
allocData(computeMaxElms(m_tableMask), tableSize);
|
||||
initHash(m_hash, tableSize);
|
||||
m_pos = ArrayData::invalid_index;
|
||||
assert(m_pos == ArrayData::invalid_index);
|
||||
return tableSize;
|
||||
}
|
||||
|
||||
HphpArray::HphpArray(uint size) : ArrayData(kHphpArray),
|
||||
m_lastE(ElmIndEmpty), m_data(nullptr), m_nextKI(0), m_hLoad(0) {
|
||||
inline void HphpArray::init(uint capacity) {
|
||||
assert(m_size == 0);
|
||||
const auto tableSize = initWithoutHash(capacity);
|
||||
initHash(m_hash, tableSize);
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
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.
|
||||
@@ -132,14 +140,18 @@ HphpArray::HphpArray(uint size, const TypedValue* values) :
|
||||
assert(m_size == 0 && m_hLoad == 0 && m_nextKI == 0);
|
||||
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;
|
||||
}
|
||||
// Initialize the leftover hash
|
||||
for (; i < size; i++) {
|
||||
hash[i] = ElmIndEmpty;
|
||||
}
|
||||
m_size = size;
|
||||
m_hLoad = size;
|
||||
m_lastE = size - 1;
|
||||
@@ -147,8 +159,8 @@ HphpArray::HphpArray(uint size, const TypedValue* values) :
|
||||
if (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();
|
||||
}
|
||||
@@ -192,7 +204,7 @@ void HphpArray::dumpDebugInfo() const {
|
||||
"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_nextKI, m_lastE, size_t(m_pos));
|
||||
fprintf(stderr, "Elements:\n");
|
||||
ssize_t lastE = m_lastE;
|
||||
Elm* elms = m_data;
|
||||
@@ -394,7 +406,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 +715,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;
|
||||
|
||||
@@ -112,7 +112,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
|
||||
@@ -295,6 +295,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 {
|
||||
@@ -356,11 +357,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)];
|
||||
@@ -438,9 +439,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);
|
||||
@@ -383,7 +387,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