日期:2014-05-17 浏览次数:20449 次
define("MAX", 100000); //simple array function simple_arr() { $i = MAX; $arr = array(); while ($i--) $arr[$i]= 'a'; } // fix array function fix_arr() { $i = MAX; $arr = new SplFixedArray(MAX); while ($i--) $arr[$i]= 'a'; } //fix array with set function fix_set_arr() { $i = MAX; $arr = new SplFixedArray(MAX); while ($i--) $arr->offsetSet($i, "a"); }
时间消耗
一般数组:0.084696054458618
固定数组:0.048405885696411
固定数组调用offsetSet方法复制:0.27650499343872
内存消耗
一般数组:9324672
固定数组:4800464
固定数组调用offsetSet方法复制:4800344
空间消耗对比
从空间和时间的效率来看,固定数组的消耗都比一般数组少了很可观。固定数组通过扩展中的内置函数offsetSet赋值比通过下标赋值时间慢的多,这个因为用扩展中的内置方法給数组赋值,php内部需要多一次函数表的查询。在空间方面,对一般数组,php内部是通过hashtable来存储,hashtable中的每一个槽位对应数组中的一个值,在php源码Zend/zend_hash.h中定义了hash相关的结构体定义和函数。
typedef struct bucket { ulong h; /* Used for numeric indexing */ uint nKeyLength; void *pData; void *pDataPtr; struct bucket *pListNext; struct bucket *pListLast; struct bucket *pNext; struct bucket *pLast; const char *arKey; } Bucket; typedef struct _hashtable { uint nTableSize; uint nTableMask; uint nNumOfElements; ulong nNextFreeElement; Bucket *pInternalPointer; /* Used for element traversal */ Bucket *pListHead; Bucket *pListTail; Bucket **arBuckets; dtor_func_t pDestructor; zend_bool persistent; unsigned char nApplyCount; zend_bool bApplyProtection; #if ZEND_DEBUG int inconsistent; #endif } HashTable
如上面代码所示,一个10个元素的php数组所站的空间是sizeof(HashTable) + 10 * size(Bucket) + 元素本身占用空间,这是代码层面的算术,其实在php内部会复杂一点,HashTable的nTableSize永远是2^n,所以即使是10个元素,php内部通过 简单算法能实现占用2^4,也即16个槽位,所以实际占用空间是sizeof(HashTable) + 16 * sizeof(Bucket) + 元素本身占用空间。(空间的计算只考虑下标是整数的情况下)
而对应固定数组直接通过用户传人的size大小初始化数组,如下面代码所示:同样10个元素的数组,所需要的空间只有10* 元素本身占用空间。
static void spl_fixedarray_init(spl_fixedarray *array, long size TSRMLS_DC) /* {{{ */ { if (size > 0) { array->size = 0; /* reset size in case ecalloc() fails */ array->elements = ecalloc(size, sizeof(zval *)); array->size = size; } else { array->elements = NULL; array->size = 0; } }
时间方面对比
对于固定数组来说,对内存的申请一步到位了,当内存不够时候会报错,当内存用不完时,也就浪费在那里。
对于普通数组,因为是动态分配数组空间,由于预先不知道要有多少元素,php初始化空数组的的时候,默认8个槽位,但槽位不够的时候,会再分配*2的空间,当元素元素的数量大于hashTbale中的nTableSize的时候,会resize和rehash hashTable,在resize和rehash的过程中,时间的消耗相当可观了。
static int zend_hash_do_resize(HashTable *ht) { Bucket **t; #ifdef ZEND_SIGNALS TSRMLS_FETCH(); #endif IS_CONSISTENT(ht); if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */ t = (Bucket **) perealloc_recoverable(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); if (t) { HANDLE_BLOCK_INTERRUPTIONS(); ht->arBuckets = t; ht->nTableSize = (ht->nTableSize << 1); ht->nTableMask = ht->nTableSize - 1; zend_hash_rehash(ht); HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } return FAILURE; } return SUCCESS; } ZEND_API int zend_hash_rehash(HashTable *ht) { Bucket *p; uint nIndex; IS_CONSISTENT(ht); if (UNEXPECTED(ht->nNumOfElements == 0)) { return SUCCESS; } memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *)); p = ht->pListHead; while (p != NULL) { nIndex = p->h & ht->nTableMask; CONNECT_TO_BUCKET_DLLIST(p, ht-