From ea53dc5af6a216b46a84944ee870c5ce18dcfe90 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 20 Apr 2010 10:49:22 +0000 Subject: [PATCH] Added an optimization which saves memory and emalloc/efree calls for empty HashTables --- NEWS | 2 ++ Zend/zend_hash.c | 57 +++++++++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/NEWS b/NEWS index d83cf0669a..d3ed2af710 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ - Upgraded bundled sqlite to version 3.6.23.1. (Ilia) - Upgraded bundled PCRE to version 8.02. (Ilia) +- Added an optimization which saves memory and emalloc/efree calls for empty + HashTables (Stas, Dmitry) - Added Tokyo Cabinet abstract DB support to ext/dba. (Michael Maclean) - Added Jenkins's one-at-a-time hash support to ext/hash. (Martin Jansen) - Added FNV-1 hash support to ext/hash. (Michael Maclean) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index d8b40872db..69b6bdc169 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -132,12 +132,18 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength) (p)->pDataPtr=NULL; \ } - +#define CHECK_INIT(ht) do { \ + if (UNEXPECTED((ht)->nTableMask == 0)) { \ + (ht)->nTableMask = (ht)->nTableSize - 1; \ + (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \ + } \ +} while (0) + +static const Bucket *uninitialized_bucket = NULL; ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; - Bucket **tmp; SET_INCONSISTENT(HT_OK); @@ -151,9 +157,9 @@ ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunctio ht->nTableSize = 1 << i; } - ht->nTableMask = ht->nTableSize - 1; + ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */ ht->pDestructor = pDestructor; - ht->arBuckets = NULL; + ht->arBuckets = (Bucket**)&uninitialized_bucket; ht->pListHead = NULL; ht->pListTail = NULL; ht->nNumOfElements = 0; @@ -162,21 +168,6 @@ ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunctio ht->persistent = persistent; ht->nApplyCount = 0; ht->bApplyProtection = 1; - - /* Uses ecalloc() so that Bucket* == NULL */ - if (persistent) { - tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *)); - if (!tmp) { - return FAILURE; - } - ht->arBuckets = tmp; - } else { - tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *)); - if (tmp) { - ht->arBuckets = tmp; - } - } - return SUCCESS; } @@ -212,6 +203,8 @@ ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKe return FAILURE; } + CHECK_INIT(ht); + h = zend_inline_hash_func(arKey, nKeyLength); nIndex = h & ht->nTableMask; @@ -278,6 +271,7 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui return zend_hash_index_update(ht, h, pData, nDataSize, pDest); } + CHECK_INIT(ht); nIndex = h & ht->nTableMask; p = ht->arBuckets[nIndex]; @@ -350,6 +344,7 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void Bucket *p; IS_CONSISTENT(ht); + CHECK_INIT(ht); if (flag & HASH_NEXT_INSERT) { h = ht->nNextFreeElement; @@ -440,6 +435,9 @@ ZEND_API int zend_hash_rehash(HashTable *ht) uint nIndex; IS_CONSISTENT(ht); + if (UNEXPECTED(ht->nNumOfElements == 0)) { + return SUCCESS; + } memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *)); p = ht->pListHead; @@ -530,7 +528,9 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } pefree(q, ht->persistent); } - pefree(ht->arBuckets, ht->persistent); + if (ht->nTableMask) { + pefree(ht->arBuckets, ht->persistent); + } SET_INCONSISTENT(HT_DESTROYED); } @@ -556,7 +556,9 @@ ZEND_API void zend_hash_clean(HashTable *ht) } pefree(q, ht->persistent); } - memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *)); + if (ht->nTableMask) { + memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *)); + } ht->pListHead = NULL; ht->pListTail = NULL; ht->nNumOfElements = 0; @@ -630,7 +632,9 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht) while (p != NULL) { p = zend_hash_apply_deleter(ht, p); } - pefree(ht->arBuckets, ht->persistent); + if (ht->nTableMask) { + pefree(ht->arBuckets, ht->persistent); + } SET_INCONSISTENT(HT_DESTROYED); } @@ -647,7 +651,9 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht) p = ht->pListTail; } - pefree(ht->arBuckets, ht->persistent); + if (ht->nTableMask) { + pefree(ht->arBuckets, ht->persistent); + } SET_INCONSISTENT(HT_DESTROYED); } @@ -1177,6 +1183,7 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const p = pos ? (*pos) : ht->pInternalPointer; IS_CONSISTENT(ht); + CHECK_INIT(ht); if (p) { if (key_type == HASH_KEY_IS_LONG) { @@ -1533,6 +1540,10 @@ void zend_hash_display(const HashTable *ht) Bucket *p; uint i; + if (UNEXPECTED(ht->nNumOfElements == 0)) { + zend_output_debug_string(0, "The hash is empty"); + return; + } for (i = 0; i < ht->nTableSize; i++) { p = ht->arBuckets[i]; while (p != NULL) { -- 2.40.0