while & list & each 组合比 array_walk 和 PHP 上的 foreach 快得多怎么可能?

How is it posible that while & list & each combination is so much faster than array_walk and foreach on PHP?

我得到了这个代码:

<?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

foreach 在每个 运行 之前重置内部数组指针,并在每一步中向前移动它。这意味着您的测试函数将如您预期的那样被称为 10 000 000。 Array walk 根本不使用内部指针,但仍然在每次调用时处理每个元素。这就是为什么时间是可比的。

但是每次使用后,每个都只会增加内部指针。它永远不会重置它(Google 手册以获取更多信息)。这意味着您只修改数组一次,并且永远不会在以后的 运行 秒内进入 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);
}

Each 每次都对数组的副本进行操作,并修改副本的内部指针,而不是原始指针。它不会打败其他功能,但会更慢,因为写时复制开销

仅仅是因为each()需要重置数组以再次迭代它。所以你在循环函数中只有一次执行。而其他人正在迭代它。

http://php.net/manual/en/function.each.php

您的结果只会产生 10000 行的 1 次迭代,而其他结果将产生 10000 行的 10000 次迭代。

$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);