首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么array_key_exists比在引用的阵列上设置的速度慢1000倍?

为什么array_key_exists比在引用的阵列上设置的速度慢1000倍?
EN

Stack Overflow用户
提问于 2011-06-14 08:29:12
回答 4查看 11.5K关注 0票数 23

我发现在检查是否在数组引用中设置了键时,array_key_existsisset慢1000倍以上。有人知道PHP是如何实现的吗?为什么会这样呢?

编辑:我添加了另一个案例,似乎指出它是调用带有引用的函数所需的开销。

基准测试示例

代码语言:javascript
复制
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 );

输出

代码语言:javascript
复制
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。

Codepad example

EN

回答 4

Stack Overflow用户

发布于 2011-06-18 20:43:27

在工作中,我有一个PHP的VM实例,其中包含一个名为VLD的PECL扩展。这使您可以从命令行执行PHP代码,而不是执行它,而是返回生成的操作码。

它很擅长回答这样的问题。

http://pecl.php.net/package/vld

以防您走这条路(如果您通常对PHP的内部工作方式感到好奇,我认为您应该这样做),您绝对应该将其安装在虚拟机上(也就是说,我不会将其安装在我试图在其上进行开发或部署的计算机上)。您将使用以下命令来使其发出声音:

代码语言:javascript
复制
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“这样的键。

票数 8
EN

Stack Overflow用户

发布于 2011-07-01 09:32:05

下面是5.2.17的array_key_exists函数的源代码。您可以看到,即使键为空,PHP也会尝试计算散列。不过,有趣的是如果你移除

代码语言:javascript
复制
// $my_array_ref[$i] = NULL;

那么它的性能就会更好。必须发生多个哈希查找。

代码语言:javascript
复制
/* {{{ 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;
    }

}
票数 3
EN

Stack Overflow用户

发布于 2011-06-14 08:50:48

不是NULL,但删除引用(= array_key_exists )会导致这种情况。我从你的脚本中把它注释掉了,结果如下:

代码语言:javascript
复制
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 )部件中删除取消设置,这是修改后的部件以供参考:

代码语言:javascript
复制
$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 );
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6337893

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档