为什么array_key_exists比引用数组上的isset慢1000倍? [英] Why is array_key_exists 1000x slower than isset on referenced arrays?

查看:80
本文介绍了为什么array_key_exists比引用数组上的isset慢1000倍?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在检查数组引用中是否设置了键时,我发现array_key_existsisset慢1000倍以上.有谁了解PHP的实现方式的人解释为什么这是真的吗?

I have found that array_key_exists is over 1000x slower than isset at check if a key is set in an array reference. Does anyone that has an understanding of how PHP is implemented explain why this is true?

我添加了另一种情况,似乎表明它是使用引用调用函数所必需的开销.

I've added another case that seems to point to it being overhead required in calling functions with a reference.

基准示例

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.

I'm on PHP 5.3.6.

键盘示例.

推荐答案

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

At work I've got a VM instance of PHP that includes a PECL extension called VLD. This lets you execute PHP code from the commandline and rather than execute it, it returns the generated opcode instead.

回答这样的问题真是太好了.

It's brilliant at answering questions like this.

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

以防万一,如果您走了这条路(并且,如果您通常对PHP的内部运行方式感到好奇,我想应该),那么您绝对应该将其安装在虚拟机上(也就是说,我不会将其安装在虚拟机上).我尝试在其上开发或部署到的计算机).这是您用来唱歌的命令:

Just in case you go this route (and if you're generally curious about how PHP works internally, i think you should) you should definitely install it on a virtual machine (that is, i wouldn't install it on a machine i'm trying to develop on or deploy to). And this is the command you'll use to make it sing:

php -d vld.execute=0 -d vld.active=1 -f foo.php

看看操作码会告诉您一个更完整的故事,但是,我有一个猜测…….大多数PHP内置函数都会复制一个Array/Object并对其执行操作(而不是复制-即时写入).最广为人知的示例是foreach().当您将数组传递给foreach()时,PHP实际上正在制作该数组的副本并在该副本上进行迭代.这就是为什么通过将数组作为参考传递给foreach会看到显着的性能优势的原因:

Looking at the opcodes will tell you a more complete story, however, I have a guess.... Most of PHP's built-ins make a copy of an Array/Object and act upon that copy (and not a copy-on-write either, an immediate copy). The most widely known example of this is foreach(). When you pass an array into foreach(), PHP is actually making a copy of that array and iterating on the copy. Whis is why you'll see a significant performance benefit by passing an array as a reference into foreach like this:

foreach($ someReallyBigArray as $ k =>& $ v)

foreach($someReallyBigArray as $k => &$v)

但是这种行为-传递像这样的显式引用-对于foreach()是唯一的.因此,如果它使array_key_exists()的检查速度更快,我会感到非常惊讶.

But this behavior -- that passing in an explicit reference like that -- is unique to foreach(). So I would be very surprised if it made an array_key_exists() check any faster.

好,回到我的意思.

大多数内置组件都会获取数组的副本并对该副本执行操作.我要冒一个完全不合格的猜测,那就是isset()是高度优化的,并且这些优化之一可能是在传入数组时不对其进行立即复制.

Most the built-ins take a copy of an array and act upon that copy. I am going to venture a completely unqualified guess that isset() is highly optimized and that one of those optimizations is perhaps to not do an immediate copy of an Array when its passed-in.

我将尝试回答您可能遇到的任何其他问题,但您可能会为google读到很多有关"zval_struct"(这是PHP内部存储每个变量的数据结构.它是C结构(请考虑一下). ..一个关联数组),该键具有值",类型",引用计数"之类的键.

I'll try to answer any other questions you may have but you could probably read a lot of you google for "zval_struct" (which is the data structure in the PHP internals which stores each variable. It's a C struct (think.. an associative array) that has keys like "value", "type", "refcount".

这篇关于为什么array_key_exists比引用数组上的isset慢1000倍?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆