看到一篇的文章(奇技淫巧一:循环加速),看到有关于count的for循环优化,后者比前者会快上很多:
//写法一:for($i= 0; $i< count($data); $i++) {}
//写法二$len= count($data);for($i= 0; $i< $len; $i++) {}
联想到PHP的strlen实现,count也不会每次都重新统计,感觉两种写法在PHP中,应该是效率相差不多。 而实际则不然,虽然都不会重新统计,但需要对该变量进行多次判断和调用不同的宏, 所以无论是strlen还是count,如胖子所言,局部变量要比函数调用快,写法二都要比写法一效率好很多。总结之:
在PHP中,变量存储在zval数据结构中,字符串和数组的存储是不同的。 字符串是不使用hashtable的,数组则使用hashtable来进行存储。
typedefstruct_zval_struct {
zvalue_value value;
zend_uint refcount;
zend_uchar type;
zend_uchar is_ref;} zval;
//其中的zval_value的定义如下:
typedefunion_zvalue_value {
longlval;
doubledval;
struct{
char<em>val;
intlen;
} str;
HashTable *ht; zend_object_value obj;
} zvalue_value;
//最关键的hashtable如下:
typedefstruct_hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /</em> Used forelement traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned charnApplyCount;
zend_bool bApplyProtection;<p></p> <h1>ifZEND_DEBUG</h1>
<pre class="">intinconsistent;</pre> <h1>endif</h1> <p>
} HashTable;
strlen的实现:
字符串的存储不使用hashtable ,而直接存储在zval_value中的str结构体中,其中*val为指针,而len为长度。 在PHP中的strlen实现中,有这样的代码:
TRLEN_PP(arg);...<p></p> <h1>define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)</h1> <p>...</p> <h1>define Z_STRLEN(zval) (zval).value.str.len</h1> <p>由此可见,strlen是不会真正去统计一个字符串的长度的,只是返回了zval中的已经存储的长度。</p>
count的实现: 在PHP的实现中,数组是一个很重要的组成部分,虽然在hashtable中有nNumOfElements 是用来存储数组的真实长度的 所以在count一个数组的时候,也不是每次都重新进行统计的,以下是部分代码(可以在/ext/standard/array.c中找到):
... if(Z_TYPE_P(array) == IS_ARRAY) { if(Z_ARRVAL_P(array)->nApplyCount > 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); return0; }<p></p> <pre class=""> cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); if(mode == COUNT_RECURSIVE) { HashPosition pos; for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void**) &element, &pos) == SUCCESS; zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos) ) { Z_ARRVAL_P(array)->nApplyCount++; cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC); Z_ARRVAL_P(array)->nApplyCount--; } }}//由此可以见,只有mode为COUNT_RECURSIVE时,数组才会重新计算长度,否则会用zend_hash_num_elements函数: ZEND_API intzend_hash_num_elements(constHashTable *ht){IS_CONSISTENT(ht); returnht->nNumOfElements;} ... returncnt;</pre> <p>}</p>
总结:
strlen会返回(zval).value.str.len;count会返回(zval).value.ht->nNumOfElements,当count($array, COUNT_RECURSIVE)时,会重新统计数组长度;局部变量还是要比函数调用快,使用for循环时,还是应该定义局部变量的;