我发现在检查是否在数组引用中设置了键时,array_key_exists
比isset
慢1000倍以上。有人知道PHP是如何实现的吗?为什么会这样呢?
编辑:我添加了另一个案例,似乎指出它是调用带有引用的函数所需的开销。
基准测试示例
function isset_( $key, array $array )
{
return isset( $array[$key] );
}
$my_array = array();
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
array_key_exists( $i, $my_array );
$my_array[$i] = 0;
}
$stop = microtime( TRUE );
print "array_key_exists( \$my_array ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
$my_array = array();
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
isset( $my_array[$i] );
$my_array[$i] = 0;
}
$stop = microtime( TRUE );
print "isset( \$my_array ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
$my_array = array();
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
isset_( $i, $my_array );
$my_array[$i] = 0;
}
$stop = microtime( TRUE );
print "isset_( \$my_array ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
$my_array = array();
$my_array_ref = &$my_array;
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
array_key_exists( $i, $my_array_ref );
$my_array_ref[$i] = 0;
}
$stop = microtime( TRUE );
print "array_key_exists( \$my_array_ref ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
$my_array = array();
$my_array_ref = &$my_array;
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
isset( $my_array_ref[$i] );
$my_array_ref[$i] = 0;
}
$stop = microtime( TRUE );
print "isset( \$my_array_ref ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
$my_array = array();
$my_array_ref = &$my_array;
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
isset_( $i, $my_array_ref );
$my_array_ref[$i] = 0;
}
$stop = microtime( TRUE );
print "isset_( \$my_array_ref ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
输出
array_key_exists( $my_array ) 0.0056459903717
isset( $my_array ) 0.00234198570251
isset_( $my_array ) 0.00539588928223
array_key_exists( $my_array_ref ) 3.64232587814 // <~ what on earth?
isset( $my_array_ref ) 0.00222992897034
isset_( $my_array_ref ) 4.12856411934 // <~ what on earth?
我使用的是PHP 5.3.6。
发布于 2011-06-18 20:43:27
在工作中,我有一个PHP的VM实例,其中包含一个名为VLD的PECL扩展。这使您可以从命令行执行PHP代码,而不是执行它,而是返回生成的操作码。
它很擅长回答这样的问题。
http://pecl.php.net/package/vld
以防您走这条路(如果您通常对PHP的内部工作方式感到好奇,我认为您应该这样做),您绝对应该将其安装在虚拟机上(也就是说,我不会将其安装在我试图在其上进行开发或部署的计算机上)。您将使用以下命令来使其发出声音:
php -d vld.execute=0 -d vld.active=1 -f foo.php
查看操作码将告诉您一个更完整的故事,然而,我有一个猜测……大多数PHP内置函数都会复制Array/Object,并对该副本执行操作(也不是写时复制,而是立即复制)。最广为人知的例子是foreach()。当您将一个数组传递给foreach()时,PHP实际上是在复制该数组并对该副本进行迭代。这就是为什么像这样将数组作为引用传递到foreach中会带来显著的性能优势:
foreach($someReallyBigArray as $k => &$v)
但是这种行为是foreach()独有的。因此,如果它能更快地进行array_key_exists()检查,我会非常惊讶。
好了,回到我要说的..
大多数内置函数都会获取数组的副本,并对该副本执行操作。我要冒一个完全没有限制的猜测,isset()是高度优化的,其中一个优化可能是在传入数组时不立即复制它。
我将尝试回答您可能有的任何其他问题,但是您可能会读到很多关于"zval_struct“的谷歌搜索(这是存储每个变量的PHP内部的数据结构。它是一个C结构(想想..一个关联数组),它有像"value","type","refcount“这样的键。
发布于 2011-07-01 09:32:05
下面是5.2.17的array_key_exists函数的源代码。您可以看到,即使键为空,PHP也会尝试计算散列。不过,有趣的是如果你移除
// $my_array_ref[$i] = NULL;
那么它的性能就会更好。必须发生多个哈希查找。
/* {{{ proto bool array_key_exists(mixed key, array search)
Checks if the given key or index exists in the array */
PHP_FUNCTION(array_key_exists)
{
zval **key, /* key to check for */
**array; /* array to check in */
if (ZEND_NUM_ARGS() != 2 ||
zend_get_parameters_ex(ZEND_NUM_ARGS(), &key, &array) == FAILURE) {
WRONG_PARAM_COUNT;
}
if (Z_TYPE_PP(array) != IS_ARRAY && Z_TYPE_PP(array) != IS_OBJECT) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument should be either an array or an object");
RETURN_FALSE;
}
switch (Z_TYPE_PP(key)) {
case IS_STRING:
if (zend_symtable_exists(HASH_OF(*array), Z_STRVAL_PP(key), Z_STRLEN_PP(key)+1)) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_LONG:
if (zend_hash_index_exists(HASH_OF(*array), Z_LVAL_PP(key))) {
RETURN_TRUE;
}
RETURN_FALSE;
case IS_NULL:
if (zend_hash_exists(HASH_OF(*array), "", 1)) {
RETURN_TRUE;
}
RETURN_FALSE;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer");
RETURN_FALSE;
}
}
发布于 2011-06-14 08:50:48
不是NULL,但删除引用(= array_key_exists )会导致这种情况。我从你的脚本中把它注释掉了,结果如下:
array_key_exists( $my_array ) 0.0059430599212646
isset( $my_array ) 0.0027170181274414
array_key_exists( $my_array_ref ) 0.0038740634918213
isset( $my_array_ref ) 0.0025200843811035
仅从array_key_exists( $my_array_ref )
部件中删除取消设置,这是修改后的部件以供参考:
$my_array = array();
$my_array_ref = &$my_array;
$start = microtime( TRUE );
for( $i = 1; $i < 10000; $i++ ) {
array_key_exists( $i, $my_array_ref );
// $my_array_ref[$i] = NULL;
}
$stop = microtime( TRUE );
print "array_key_exists( \$my_array_ref ) ".($stop-$start).PHP_EOL;
unset( $my_array, $my_array_ref, $start, $stop, $i );
https://stackoverflow.com/questions/6337893
复制相似问题