PHP源代码数组统计count分析
2015-01-24信息快讯网
偶然在百度知道中看到有个同学问起count及strlen的效率问题,好吧这个问题我当初没理解透彻,认为其不属两个不一样的东西不可比较,后来看了楼主的回复才反应过来,所以自己也去找了下源码查看下。现在总结下查看到的结果并记录之。
zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下://文件1:zend/zend.h /* * zval */ typedef struct _zval_struct zval; ... typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; //hash表的结构如下 //文件2:zend/zend_hash.h 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;
一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:
//文件3:zend/zend_operators.php #define Z_STRLEN(zval) (zval).value.str.len ... #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p) ... #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代码如下:
//文件4:ext/standard/array.c PHP_FUNCTION(count) { zval *array; long mode = COUNT_NORMAL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) { return; } switch (Z_TYPE_P(array)) { case IS_NULL: RETURN_LONG(0); break; case IS_ARRAY: RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC)); break; ..... //php_count_recursive的实现 static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ { long cnt = 0; zval **element; if (Z_TYPE_P(array) == IS_ARRAY) { //错误处理 if (Z_ARRVAL_P(array)->nApplyCount > 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); return 0; } //通过zend_hash_num_elements直接获得长度 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--; } } } return cnt; } //文件5:zend/zend_hash.c //zend_hash_num_elements的实现 ZEND_API int zend_hash_num_elements(const HashTable *ht) { IS_CONSISTENT(ht); return ht->nNumOfElements; }
file_get_contents获取不到网页内容的解决方法
PHP屏蔽蜘蛛访问代码及常用搜索引擎的HTTP_USER_AGENT
利用PHP扩展vld查看PHP opcode操作步骤
CI框架源码阅读,系统常量文件constants.php的配置
PHP中通过HTTP_USER_AGENT判断是否为手机移动终端的函数代码
PHP5.4中json_encode中文转码的变化小结
PHP基础教程(php入门基础教程)一些code代码
PDO版本问题 Invalid parameter number: no parameters were bound
关于mysql字符集设置了character_set_client=binary 在gbk情况下会出现表描述是乱码的情况
Could not load type System.ServiceModel.Activation.HttpModule解决办法
php中unlink()、mkdir()、rmdir()等方法的使用介绍
PHP提示Notice: Undefined variable的解决办法
php模拟js函数unescape的函数代码
用来解析.htgroup文件的PHP类
PHP+Mysql日期时间如何转换(UNIX时间戳和格式化日期)
PHP中创建空文件的代码[file_put_contents vs touch]
php数组函数序列 之array_count_values() 统计数组中所有值出现的次数函数
应用开发中涉及到的css和php笔记分享
linux下为php添加curl扩展的方法
php中修改浏览器的User-Agent来伪装你的浏览器和操作系统
PHP通过iconv将字符串从GBK转换为UTF8字符集
PHP 删除文件与文件夹操作 unlink()与rmdir()这两个函数的使用
PHP Undefined index报错的修复方法
php自定义函数call_user_func和call_user_func_array详解
PHP setcookie设置Cookie用法(及设置无效的问题)
php array_intersect比array_diff快(附详细的使用说明)
php header Content-Type类型小结
php中关于codeigniter的xmlrpc的类在进行数据交换时的类型问题
yii框架源码分析之创建controller代码
php设计模式 Interpreter(解释器模式)
Warning: session_destroy() : Trying to destroy uninitialized sessionq错误
php中session_unset与session_destroy的区别分析
使用GROUP BY的时候如何统计记录条数 COUNT(*) DISTINCT
mysql_num_rows VS COUNT 效率问题分析
php expects parameter 1 to be resource, array given 错误