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 错误
©2014-2024 dbsqp.com