Browse Source

Use special flag (HASH_FLAG_INITIALIZED) instead of (nTableMask == 0) to indicate that HashTable is allocated.

Make nTableMask to be 0 for packed arrays.
Remove checks fo HASH_FLAG_PACKED in zend_hash_find/zend_hash_del and family (string keys are resolved through uninitialized_bucket).
Change HashTable layout for better locality.
pull/956/head
Dmitry Stogov 12 years ago
parent
commit
37466b0368
  1. 84
      Zend/zend_hash.c
  2. 1
      Zend/zend_hash.h
  3. 18
      Zend/zend_types.h
  4. 12
      ext/opcache/zend_accelerator_util_funcs.c
  5. 4
      ext/opcache/zend_persist.c
  6. 2
      ext/opcache/zend_persist_calc.c

84
Zend/zend_hash.c

@ -82,18 +82,19 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
static void zend_hash_do_resize(HashTable *ht);
#define CHECK_INIT(ht, packed) do { \
if (UNEXPECTED((ht)->nTableMask == 0)) { \
#define CHECK_INIT(ht, packed) do { \
if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) { \
if (packed) { \
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
(ht)->u.flags |= HASH_FLAG_PACKED; \
(ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; \
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
} else { \
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
(ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); \
memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); \
(ht)->u.flags |= HASH_FLAG_INITIALIZED; \
(ht)->nTableMask = (ht)->nTableSize - 1; \
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
(ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); \
memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); \
} \
(ht)->nTableMask = (ht)->nTableSize - 1; \
} \
} \
} while (0)
static const uint32_t uninitialized_bucket = {INVALID_IDX};
@ -129,7 +130,7 @@ ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestru
# endif
#endif
ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
ht->nTableMask = 0;
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
@ -145,7 +146,6 @@ static void zend_hash_packed_grow(HashTable *ht)
HANDLE_BLOCK_INTERRUPTIONS();
ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket), 0, ht->u.flags & HASH_FLAG_PERSISTENT);
ht->nTableSize = (ht->nTableSize << 1);
ht->nTableMask = ht->nTableSize - 1;
HANDLE_UNBLOCK_INTERRUPTIONS();
}
@ -170,6 +170,7 @@ ZEND_API void zend_hash_to_packed(HashTable *ht)
{
HANDLE_BLOCK_INTERRUPTIONS();
ht->u.flags |= HASH_FLAG_PACKED;
ht->nTableMask = 0;
ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
ht->arHash = (uint32_t*)&uninitialized_bucket;
HANDLE_UNBLOCK_INTERRUPTIONS();
@ -267,7 +268,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s
IS_CONSISTENT(ht);
if (UNEXPECTED(ht->nTableMask == 0)) {
if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
CHECK_INIT(ht, 0);
goto add_to_hash;
} else if (ht->u.flags & HASH_FLAG_PACKED) {
@ -418,7 +419,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
IS_CONSISTENT(ht);
if (UNEXPECTED(ht->nTableMask == 0)) {
if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
CHECK_INIT(ht, h < ht->nTableSize);
if (h < ht->nTableSize) {
p = ht->arData + h;
@ -587,7 +588,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
IS_CONSISTENT(ht);
if (UNEXPECTED(ht->nNumOfElements == 0)) {
if (ht->nTableMask) {
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
}
return SUCCESS;
@ -684,10 +685,6 @@ ZEND_API int zend_hash_del(HashTable *ht, zend_string *key)
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return FAILURE;
}
h = zend_string_hash_val(key);
nIndex = h & ht->nTableMask;
@ -718,10 +715,6 @@ ZEND_API int zend_hash_del_ind(HashTable *ht, zend_string *key)
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return FAILURE;
}
h = zend_string_hash_val(key);
nIndex = h & ht->nTableMask;
@ -765,10 +758,6 @@ ZEND_API int zend_hash_str_del(HashTable *ht, const char *str, size_t len)
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return FAILURE;
}
h = zend_inline_hash_func(str, len);
nIndex = h & ht->nTableMask;
@ -905,7 +894,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
} while (++p != end);
}
}
} else if (EXPECTED(!ht->nTableMask)) {
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
return;
}
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
@ -947,7 +936,7 @@ ZEND_API void zend_array_destroy(HashTable *ht)
}
SET_INCONSISTENT(HT_DESTROYED);
} else if (EXPECTED(!ht->nTableMask)) {
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
return;
}
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
@ -990,16 +979,14 @@ ZEND_API void zend_hash_clean(HashTable *ht)
} while (++p != end);
}
}
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
}
}
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->nInternalPointer = INVALID_IDX;
if (ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
}
}
}
ZEND_API void zend_symtable_clean(HashTable *ht)
@ -1019,16 +1006,14 @@ ZEND_API void zend_symtable_clean(HashTable *ht)
}
}
} while (++p != end);
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
}
}
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->nInternalPointer = INVALID_IDX;
if (ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
}
}
}
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
@ -1043,7 +1028,7 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
if (Z_TYPE(p->val) == IS_UNDEF) continue;
_zend_hash_del_el(ht, idx, p);
}
if (ht->nTableMask) {
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
}
@ -1065,7 +1050,7 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
_zend_hash_del_el(ht, idx, p);
}
if (ht->nTableMask) {
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
}
@ -1257,7 +1242,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source)
target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
target_idx = 0;
if (target->nTableMask) {
if (target->u.flags & HASH_FLAG_INITIALIZED) {
if (target->u.flags & HASH_FLAG_PACKED) {
target->nNumUsed = source->nNumUsed;
target->nNumOfElements = source->nNumOfElements;
@ -1448,10 +1433,6 @@ ZEND_API zval *zend_hash_find(const HashTable *ht, zend_string *key)
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return NULL;
}
p = zend_hash_find_bucket(ht, key);
return p ? &p->val : NULL;
}
@ -1463,10 +1444,6 @@ ZEND_API zval *zend_hash_str_find(const HashTable *ht, const char *str, size_t l
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return NULL;
}
h = zend_inline_hash_func(str, len);
p = zend_hash_str_find_bucket(ht, str, len, h);
return p ? &p->val : NULL;
@ -1478,10 +1455,6 @@ ZEND_API zend_bool zend_hash_exists(const HashTable *ht, zend_string *key)
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return 0;
}
p = zend_hash_find_bucket(ht, key);
return p ? 1 : 0;
}
@ -1493,10 +1466,6 @@ ZEND_API zend_bool zend_hash_str_exists(const HashTable *ht, const char *str, si
IS_CONSISTENT(ht);
if (ht->u.flags & HASH_FLAG_PACKED) {
return 0;
}
h = zend_inline_hash_func(str, len);
p = zend_hash_str_find_bucket(ht, str, len, h);
return p ? 1 : 0;
@ -1744,6 +1713,7 @@ ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
} else {
if (renumber) {
ht->u.flags |= HASH_FLAG_PACKED;
ht->nTableMask = 0;
ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
ht->arHash = (uint32_t*)&uninitialized_bucket;
} else {

1
Zend/zend_hash.h

@ -39,6 +39,7 @@
#define HASH_FLAG_PERSISTENT (1<<0)
#define HASH_FLAG_APPLY_PROTECTION (1<<1)
#define HASH_FLAG_PACKED (1<<2)
#define HASH_FLAG_INITIALIZED (1<<3)
#define HASH_MASK_CONSISTENCY 0x60

18
Zend/zend_types.h

@ -157,15 +157,6 @@ typedef struct _Bucket {
} Bucket;
typedef struct _HashTable {
uint32_t nTableSize;
uint32_t nTableMask;
uint32_t nNumUsed;
uint32_t nNumOfElements;
zend_long nNextFreeElement;
Bucket *arData;
uint32_t *arHash;
dtor_func_t pDestructor;
uint32_t nInternalPointer;
union {
struct {
ZEND_ENDIAN_LOHI_3(
@ -175,6 +166,15 @@ typedef struct _HashTable {
} v;
uint32_t flags;
} u;
uint32_t nTableSize;
uint32_t nTableMask;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
Bucket *arData;
uint32_t *arHash;
dtor_func_t pDestructor;
} HashTable;
struct _zend_array {

12
ext/opcache/zend_accelerator_util_funcs.c

@ -301,12 +301,12 @@ static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind)
ht->nNumOfElements = source->nNumOfElements;
ht->nNextFreeElement = source->nNextFreeElement;
ht->pDestructor = ZVAL_PTR_DTOR;
ht->u.flags = HASH_FLAG_APPLY_PROTECTION;
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION;
ht->arData = NULL;
ht->arHash = NULL;
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
ht->arHash = (uint32_t*)&uninitialized_bucket;
return;
}
@ -382,10 +382,10 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class
ht->nNumOfElements = source->nNumOfElements;
ht->nNextFreeElement = source->nNextFreeElement;
ht->pDestructor = ZEND_FUNCTION_DTOR;
ht->u.flags = 0;
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
ht->arHash = (uint32_t*)&uninitialized_bucket;
return;
}
@ -458,10 +458,10 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla
ht->nNumOfElements = source->nNumOfElements;
ht->nNextFreeElement = source->nNextFreeElement;
ht->pDestructor = zend_destroy_property_info;
ht->u.flags = 0;
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
ht->arHash = (uint32_t*)&uninitialized_bucket;
return;
}

4
ext/opcache/zend_persist.c

@ -74,7 +74,7 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement
uint idx;
Bucket *p;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
ht->arHash = (uint32_t*)&uninitialized_bucket;
return;
}
@ -111,7 +111,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
uint idx;
Bucket *p;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
ht->arHash = (uint32_t*)&uninitialized_bucket;
return;
}

2
ext/opcache/zend_persist_calc.c

@ -54,7 +54,7 @@ static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval *
uint idx;
Bucket *p;
if (!ht->nTableMask) {
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
return;
}
if (ht->u.flags & HASH_FLAG_PACKED) {

Loading…
Cancel
Save