php strlen 和 count 底层实现 (局部变量效率优先)

作者:暗夜星辰 发布于:2012-6-14 17:22 Thursday 分类:PHP学习笔记

原文地址:

http://www.zhangabc.com/2011/01/05/php-internal-count-strlen-implement/

昨天看到一篇的文章(奇技淫巧一:循环加速),看到有关于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), &amp;pos); zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void**) &amp;element, &amp;pos) == SUCCESS; zend_hash_move_forward_ex(Z_ARRVAL_P(array), &amp;pos) ) { Z_ARRVAL_P(array)-&gt;nApplyCount++; cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC); Z_ARRVAL_P(array)-&gt;nApplyCount--; } }}//由此可以见,只有mode为COUNT_RECURSIVE时,数组才会重新计算长度,否则会用zend_hash_num_elements函数: ZEND_API intzend_hash_num_elements(constHashTable *ht){IS_CONSISTENT(ht); returnht-&gt;nNumOfElements;} ... returncnt;</pre> <p>}</p>

总结:

strlen会返回(zval).value.str.len;count会返回(zval).value.ht->nNumOfElements,当count($array, COUNT_RECURSIVE)时,会重新统计数组长度;局部变量还是要比函数调用快,使用for循环时,还是应该定义局部变量的; 

标签: PHP mod

发表评论:

©2012-2014 woodbunny.com 木头♥兔子的小屋 sitemap