• PHP源代码数组统计count分析


         偶然在百度知道中看到有个同学问起count及strlen的效率《http://zhidao.baidu.com/question/300773887.html》的问题,好吧这个问题我当初没理解透彻,认为其不属两个不一样的东西不可比较,后来看了楼主的回复才反应过来,所以自己也去找了下源码查看下。现在总结下查看到的结果并记录之。

         zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:

     1 //文件1:zend/zend.h
    2 /*
    3 * zval
    4 */
    5 typedef struct _zval_struct zval;
    6 ...
    7 typedef union _zvalue_value {
    8 long lval; /* long value */
    9 double dval; /* double value */
    10 struct {
    11 char *val;
    12 int len;
    13 } str;
    14 HashTable *ht; /* hash table value */
    15 zend_object_value obj;
    16 } zvalue_value;
    17
    18 struct _zval_struct {
    19 /* Variable information */
    20 zvalue_value value; /* value */
    21 zend_uint refcount__gc;
    22 zend_uchar type; /* active type */
    23 zend_uchar is_ref__gc;
    24 };
    25 //hash表的结构如下
    26 //文件2:zend/zend_hash.h
    27 typedef struct _hashtable {
    28 uint nTableSize;
    29 uint nTableMask;
    30 uint nNumOfElements;
    31 ulong nNextFreeElement;
    32 Bucket *pInternalPointer; /* Used for element traversal */
    33 Bucket *pListHead;
    34 Bucket *pListTail;
    35 Bucket **arBuckets;
    36 dtor_func_t pDestructor;
    37 zend_bool persistent;
    38 unsigned char nApplyCount;
    39 zend_bool bApplyProtection;
    40 #if ZEND_DEBUG
    41 int inconsistent;
    42 #endif
    43 } HashTable;

            一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:

    1 //文件3:zend/zend_operators.php
    2 #define Z_STRLEN(zval) (zval).value.str.len
    3 ...
    4 #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
    5 ...
    6 #define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)

          而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:

     1 //文件4:ext/standard/array.c
    2 PHP_FUNCTION(count)
    3 {
    4 zval *array;
    5 long mode = COUNT_NORMAL;
    6
    7 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
    8 return;
    9 }
    10
    11 switch (Z_TYPE_P(array)) {
    12 case IS_NULL:
    13 RETURN_LONG(0);
    14 break;
    15 case IS_ARRAY:
    16 RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
    17 break;
    18 .....
    19
    20 //php_count_recursive的实现
    21 static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
    22 {
    23 long cnt = 0;
    24 zval **element;
    25
    26 if (Z_TYPE_P(array) == IS_ARRAY) {
    27 //错误处理
    28 if (Z_ARRVAL_P(array)->nApplyCount > 1) {
    29 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
    30 return 0;
    31 }
    32 //通过zend_hash_num_elements直接获得长度
    33 cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
    34
    35 //如果指定了需要重新统计,则会进入一次循环统计
    36 if (mode == COUNT_RECURSIVE) {
    37 HashPosition pos;
    38
    39 for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
    40 zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
    41 zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
    42 ) {
    43 Z_ARRVAL_P(array)->nApplyCount++;
    44 cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
    45 Z_ARRVAL_P(array)->nApplyCount--;
    46 }
    47 }
    48 }
    49
    50 return cnt;
    51 }
    52
    53 //文件5:zend/zend_hash.c
    54 //zend_hash_num_elements的实现
    55 ZEND_API int zend_hash_num_elements(const HashTable *ht)
    56 {
    57 IS_CONSISTENT(ht);
    58
    59 return ht->nNumOfElements;
    60 }

  • 相关阅读:
    golang实现dns域名解析(一)
    互联网协议入门(一)(转)
    DNS入门(转)
    随笔:Golang 时间Time
    mysql查询某一个字段是否包含中文字符
    screen状态变Attached连接会话失败
    golang :连接数据库闲置断线的问题
    神奇的GO语言:空接口(interface)
    Go语言:变参函数
    go语言:函数参数传递详解
  • 原文地址:https://www.cnblogs.com/xiaoyaoxia/p/2125343.html
Copyright © 2020-2023  润新知