From: Dmitry Stogov Date: Fri, 24 Apr 2015 06:34:50 +0000 (+0300) Subject: Added HashTable flag HASH_FLAG_STATIC_KEYS that is maintaned to be set if all hash... X-Git-Tag: PRE_PHP7_NSAPI_REMOVAL~171 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=62656877688abf51fcac74ec342483ca520ee15d;p=php Added HashTable flag HASH_FLAG_STATIC_KEYS that is maintaned to be set if all hash table keys are numbers or interned strings. Take this flag into account when copy or destroy HashTable to avoud useless checks for each key. --- diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 5aa8620683..c187aef200 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -147,7 +147,7 @@ ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_ { GC_REFCOUNT(ht) = 1; GC_TYPE_INFO(ht) = IS_ARRAY; - ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_APPLY_PROTECTION; + ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS; ht->nTableSize = zend_hash_check_size(nSize); ht->nTableMask = HT_MIN_MASK; HT_SET_DATA_ADDR(ht, &uninitialized_bucket); @@ -201,7 +201,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht) HT_ASSERT(GC_REFCOUNT(ht) == 1); HANDLE_BLOCK_INTERRUPTIONS(); - ht->u.flags |= HASH_FLAG_PACKED; + ht->u.flags |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; ht->nTableMask = HT_MIN_MASK; HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT)); HT_HASH_RESET_PACKED(ht); @@ -518,7 +518,10 @@ add_to_hash: p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; - zend_string_addref(key); + if (!IS_INTERNED(key)) { + zend_string_addref(key); + ht->u.flags &= ~HASH_FLAG_STATIC_KEYS; + } ZVAL_COPY_VALUE(&p->val, pData); nIndex = h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); @@ -1127,7 +1130,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) if (ht->pDestructor) { SET_INCONSISTENT(HT_IS_DESTROYING); - if (ht->u.flags & HASH_FLAG_PACKED) { + if (ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS)) { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { ht->pDestructor(&p->val); @@ -1146,7 +1149,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) SET_INCONSISTENT(HT_DESTROYED); } else { - if (!(ht->u.flags & HASH_FLAG_PACKED)) { + if (!(ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS))) { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { if (EXPECTED(p->key)) { @@ -1182,7 +1185,7 @@ ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht) end = p + ht->nNumUsed; SET_INCONSISTENT(HT_IS_DESTROYING); - if (ht->u.flags & HASH_FLAG_PACKED) { + if (ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS)) { do { i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); } while (++p != end); @@ -1217,7 +1220,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) p = ht->arData; end = p + ht->nNumUsed; if (ht->pDestructor) { - if (ht->u.flags & HASH_FLAG_PACKED) { + if (ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS)) { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { ht->pDestructor(&p->val); @@ -1234,7 +1237,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) } while (++p != end); } } else { - if (!(ht->u.flags & HASH_FLAG_PACKED)) { + if (!(ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS))) { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { if (EXPECTED(p->key)) { @@ -1264,17 +1267,23 @@ ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht) if (ht->nNumUsed) { p = ht->arData; end = p + ht->nNumUsed; - do { - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { - i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (ht->u.flags & HASH_FLAG_STATIC_KEYS) { + do { + if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { + ht->pDestructor(&p->val); } - } - } while (++p != end); - if (!(ht->u.flags & HASH_FLAG_PACKED)) { - HT_HASH_RESET(ht); + } while (++p != end); + } else { + do { + if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { + i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); + if (EXPECTED(p->key)) { + zend_string_release(p->key); + } + } + } while (++p != end); } + HT_HASH_RESET(ht); } ht->nNumUsed = 0; ht->nNumOfElements = 0; @@ -1521,7 +1530,22 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target_idx = 0; if (target->u.flags & HASH_FLAG_INITIALIZED) { - if (target->u.flags & HASH_FLAG_PACKED) { + if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { + target->nNumUsed = source->nNumUsed; + target->nNumOfElements = source->nNumOfElements; + target->nNextFreeElement = source->nNextFreeElement; + HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); + target->nInternalPointer = source->nInternalPointer; + memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source)); + if (target->nNumOfElements > 0 && + target->nInternalPointer == HT_INVALID_IDX) { + idx = 0; + while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { + idx++; + } + target->nInternalPointer = idx; + } + } else if (target->u.flags & HASH_FLAG_PACKED) { target->nNumUsed = source->nNumUsed; target->nNumOfElements = source->nNumOfElements; target->nNextFreeElement = source->nNextFreeElement; @@ -1568,6 +1592,50 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) } target->nInternalPointer = idx; } + } else if (target->u.flags & HASH_FLAG_STATIC_KEYS) { + target->nNextFreeElement = source->nNextFreeElement; + HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); + HT_HASH_RESET(target); + + for (idx = 0; idx < source->nNumUsed; idx++) { + p = source->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + /* INDIRECT element may point to UNDEF-ined slots */ + data = &p->val; + if (Z_TYPE_P(data) == IS_INDIRECT) { + data = Z_INDIRECT_P(data); + if (Z_TYPE_P(data) == IS_UNDEF) { + continue; + } + } + + if (source->nInternalPointer == idx) { + target->nInternalPointer = target_idx; + } + + q = target->arData + target_idx; + q->h = p->h; + q->key = p->key; + nIndex = q->h | target->nTableMask; + Z_NEXT(q->val) = HT_HASH(target, nIndex); + HT_HASH(target, nIndex) = HT_IDX_TO_HASH(target_idx); + if (Z_OPT_REFCOUNTED_P(data)) { + if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) { + ZVAL_COPY(&q->val, Z_REFVAL_P(data)); + } else { + ZVAL_COPY(&q->val, data); + } + } else { + ZVAL_COPY_VALUE(&q->val, data); + } + target_idx++; + } + target->nNumUsed = target_idx; + target->nNumOfElements = target_idx; + if (target->nNumOfElements > 0 && + target->nInternalPointer == HT_INVALID_IDX) { + target->nInternalPointer = 0; + } } else { target->nNextFreeElement = source->nNextFreeElement; HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); @@ -2043,7 +2111,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, co void *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; - ht->u.flags |= HASH_FLAG_PACKED; + ht->u.flags |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS; ht->nTableMask = HT_MIN_MASK; HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), ht->u.flags & HASH_FLAG_PERSISTENT & HASH_FLAG_PERSISTENT)); memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed); diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 7ef9242ad2..419b26149e 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -38,6 +38,7 @@ #define HASH_FLAG_APPLY_PROTECTION (1<<1) #define HASH_FLAG_PACKED (1<<2) #define HASH_FLAG_INITIALIZED (1<<3) +#define HASH_FLAG_STATIC_KEYS (1<<4) #define HASH_MASK_CONSISTENCY 0x60 @@ -879,8 +880,13 @@ static zend_always_inline void _zend_hash_append(HashTable *ht, zend_string *key Bucket *p = ht->arData + idx; ZVAL_COPY_VALUE(&p->val, zv); - p->key = zend_string_copy(key); - p->h = zend_string_hash_val(key); + if (!IS_INTERNED(key)) { + ht->u.flags &= ~HASH_FLAG_STATIC_KEYS; + zend_string_addref(key); + zend_string_hash_val(key); + } + p->key = key; + p->h = key->h; nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); @@ -895,8 +901,13 @@ static zend_always_inline void _zend_hash_append_ptr(HashTable *ht, zend_string Bucket *p = ht->arData + idx; ZVAL_PTR(&p->val, ptr); - p->key = zend_string_copy(key); - p->h = zend_string_hash_val(key); + if (!IS_INTERNED(key)) { + ht->u.flags &= ~HASH_FLAG_STATIC_KEYS; + zend_string_addref(key); + zend_string_hash_val(key); + } + p->key = key; + p->h = key->h; nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); @@ -911,8 +922,13 @@ static zend_always_inline void _zend_hash_append_ind(HashTable *ht, zend_string Bucket *p = ht->arData + idx; ZVAL_INDIRECT(&p->val, ptr); - p->key = zend_string_copy(key); - p->h = zend_string_hash_val(key); + if (!IS_INTERNED(key)) { + ht->u.flags &= ~HASH_FLAG_STATIC_KEYS; + zend_string_addref(key); + zend_string_hash_val(key); + } + p->key = key; + p->h = key->h; nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 2b94e3da7b..b8a8694b84 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -268,6 +268,7 @@ static void zend_persist_zval(zval *z) Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE; GC_REFCOUNT(Z_COUNTED_P(z)) = 2; GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE; + Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS; Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } @@ -323,6 +324,7 @@ static void zend_persist_zval_static(zval *z) Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE; GC_REFCOUNT(Z_COUNTED_P(z)) = 2; GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE; + Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS; Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } @@ -380,6 +382,7 @@ static void zend_persist_zval_const(zval *z) Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE; GC_REFCOUNT(Z_COUNTED_P(z)) = 2; GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE; + Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS; Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } @@ -445,6 +448,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc /* make immutable array */ GC_REFCOUNT(op_array->static_variables) = 2; GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); + op_array->static_variables->u.flags |= HASH_FLAG_STATIC_KEYS; op_array->static_variables->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } }