当使用PHP中的& list & each组合时,为什么比array_walk和foreach更快?

3
我得到了这段代码:
<?php
// Test results
$array1 = test('array_walk');
$array2 = test('array_walk_list_each');
$array3 = test('array_walk_foreach1');
$array4 = test('array_walk_foreach2');

// Check arrays for equal
var_dump($array1 == $array2, $array1 == $array3, $array1 == $array4);

// Test function 1
function array_walk_list_each(&$array, $function, $userData = null) {
    while ( list($key, $value) = each($array) )
        $function($array[$key], $key, $userData);
}

// Test function 2
function array_walk_foreach1(&$array, $function, $userData = null) {
    foreach ($array as $key => &$value )
        $function($value, $key, $userData);
}

// Test function 3
function array_walk_foreach2(&$array, $function, $userData = null) {
    foreach ($array as $key => $value )
        $function($array[$key], $key, $userData);
}

function some_function(&$value, $key, $userData) {
    $value = "$key => $userData";
}

function test($function, $count = 10000, $arrayElements = 1000) {
    echo $function, ' ... ';
    $array = array_fill(0, $arrayElements, "some text value");

    $timer = microtime(true);
    for( $i = 0; ++$i < $count; )
        $function($array, 'some_function', 'some user data');
    printf("%.3f sec\n", microtime(true) - $timer);

    return $array;
}

这个输出对我来说很难理解:

array_walk ... 1.024 sec
array_walk_list_each ... 0.002 sec
array_walk_foreach1 ... 1.135 sec
array_walk_foreach2 ... 1.359 sec
bool(true)
bool(true)
bool(true)

这些函数之间的性能差异几乎是一个笑话。

怎么可能呢?我做错了什么吗?

我正在使用PHP 7.0从终端运行脚本。


3
在这四个函数中,你认为你需要迭代数组多少次?你觉得这对于输掉的三个函数公平吗?在获胜的功能中使用reset($array)可以改变这一点吗?SIA有点自鸣得意。 - jh1711
1
@jh1711 发布一个回答来解释这个问题。 - Barmar
1
@Barmer 好的,我会的。 - jh1711
@jh1711,“SIA”是什么意思? - Andy Lester
@AndyLester,提前道歉。但我不认为这是一个常见的缩写。我只是在打字时有点懒。 - jh1711
2个回答

2

foreach在每次运行之前都会重置内部数组指针,并在每一步中向前移动。这意味着您的测试函数将像您预期的那样被调用10,000,000次。Array walk完全不使用内部指针,但仍在每次调用时处理每个元素。这就是为什么时间可以相当的原因。

但是each只是在每次使用后递增内部指针。它从未重置它(请查看手册以获取更多信息)。这意味着您仅修改了数组一次,而在以后的运行中从未进入while循环。由于您的some_function是幂等的,因此您的等于检查通过,但时间要短得多。

编辑添加:重置不必明确。考虑以下代码:

function array_walk_list_each_copy(&$array, $function, $userData = null) {
  $a = $array;
  while ( list($key, $value) = each($a) ) 
    $function($array[$key], $key, $userData);
}

每次都在数组的副本上工作,并修改副本的内部指针,而不是原始数组。这种方法不会比其他函数更快,甚至会更慢,因为需要进行写时复制的开销。

我怎么会得到四个相等的数组? - perodriguezl
你的函数在每次运行时都会将“一些用户数据”分配给每个值。这意味着数组在第一次运行后实际上并没有改变(在数学中,这样的函数称为幂等函数)。这就是为什么所有四个数组都是相等的原因。如果你使用不同的函数(例如 $value = $value++),那么这些数组将会不同。 - jh1711

2
仅仅因为each()需要重置数组以便再次迭代。因此,您在循环函数中只有一个执行。而其他人则正在迭代。您的结果将仅产生10000行的1次迭代,而其他人将进行10000行的10000次迭代。 http://php.net/manual/en/function.each.php
$array = array_fill(0, 10000, uniqid('', false));
$fill = [];
$fill2 = [];
$timer = microtime(true);
for ($i = 0; $i < 10; $i++) {
    while (list($key, $value) = each($array)) {
        $fill[] = 'value';
    }
}
printf("While list each %.3f sec\n", microtime(true) - $timer);
$timer = microtime(true);
for ($i = 0; $i < 10; $i++) {
    foreach ($array as $key => $value) {
        $fill2[] = 'value';
    }
}
printf("Foreach %.3f sec\n", microtime(true) - $timer);
var_dump(count($fill), count($fill2));

结果: https://3v4l.org/bvNdO


为了使所有函数的结果相同,您需要更改array_walk_list_each函数。

 while ( list($key, $value) = each($array) ){
    $function($array[$key], $key, $userData);
 }
 reset($array);

但是看,我在所有的函数结果中得到了相同的4个数组,这怎么可能呢? - perodriguezl
@perodriguezl 在任何一个函数的第一次迭代之后,你的数组都没有被改变。它们中没有任何东西被添加,你只是改变了最初的10,000个值集合,其他函数也是如此。如果你这样做 $value = "$key => $userData" . ++$i; 你会发现增加的值与其他函数不匹配。 - Will B.
你实际上正在执行这个操作:https://3v4l.org/Ohr76,但这是实际结果:https://3v4l.org/KIpKU。 - Will B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接