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

Question

I got this code:

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

The Output of this is very difficult for me to understand:

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)

the performance difference between these functions it's almost a joke.

How is it possible? am I doing something wrong?

I am running the script from the terminal using PHP 7.0


Show source
| php   | arrays   | foreach   | array-map   | array-walk   2017-08-11 23:08 2 Answers

Answers to How is it posible that while &amp; list &amp; each combination is so much faster than array_walk and foreach on PHP? ( 2 )

  1. 2017-08-12 00:08

    foreach resets the internal array pointer before every run and moves it forward in every step. That means your test function will be called 10 000 000 as you probably expect. Array walk doesn't use the internal pointer at all but still treats every element on each call. That's why the time is comparable.

    But each just increments the internal pointer after each use. It never resets it (Google the manual for more information). That means that you only modify the array once, and never enter the while loop on later runs. Since your some_function is idempotent your check for equal passes, but the time is much shorter.

    Edit to add: The reset doesn't have to be explicit. Consider the this code:

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

    Each works on a copy of the array every time, and modifies the internal pointer of the copy, not the original. It won't beat the other functions, but be even slower, because of the copy-on-write overhead

  2. 2017-08-12 00:08

    Simply because each() needs to reset the array to iterate over it again. So you have a single execution within the loop function. While the others are iterating over it.

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

    Your result would produce only 1 iteration of 10000 rows, while the others would be 10000 iterations of 10000 rows.

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

    Result: https://3v4l.org/bvNdO


    To get identical results for all of the functions you would need to change the array_walk_list_each function.

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

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

◀ Go back