array_walk() performance test.
By: Steve Clifton
Tags: Arrays, Php Development, PHP

Apply a user supplied function to every member of an array

As developers, we are always trying to write better, more concise code with the intentions of a) making it easier to read, and b) making it perform faster. (At least I assume thats what other developers are trying to do too). After a recent code review, I had some feedback was that some parts could be more concise, so I have been reading over and running lots of tests with a variety of php array functions. I have half-written a quick TLDR reference guide (not published yet) for some of the newer ones (new to me, not PHP) which will provide a good resource to see each methods I/O. One really cool one in particular is array_column which I know ill be using in my day to day life a fair bit now!

I have seen the array_walk() function used a few times throughout the various code bases I’ve worked with, but never really took the time to try it out and see if it can provide any benefit to my coding style. In premise, it looks to be not so different from the foreach() method we would have written a million times before, so I decided to run a few scenarios and see if there is any reason/place I could use the array_walk() instead of the classic foreach() loop.

Getting started

Super simple test to get familiar with the function. Starting with an array filled with scalar values (bool, int, float, string), simply looping over each one and appending some text to them . As values are passed by VALUE, this means that if you want to be able to modify the value you will need to use the ‘&’ BEFORE the $item variable.

$arr1 = ['jilly', 'milo', 'twink']; array_walk($arr1, function(&$item, $key) {

$item .= ' the meow cat.';

}); /* Output */

Array

(

[0] => jilly the meow cat.

[1] => milo the meow cat.

[2] => twink the meow cat.

)

Seems simple enough, now let’s look at a slightly different problem.

Test

In this test, I wanted to compare the speed that array_walk() will perform at compared with foreach(). This required a large array, so I created one containing 300,000 objects, and then used each function to simply add a new property to the object called fullname which concatenates the first and last names, as well as a incremented $i value (simply to show how to pass a variable to the function from outside of the array). Each test was run in its own script, using PHP 7.3.2 and times were averaged over 10 tests. Also I am not checking to see if the values exist, I know they do in this case ¯\_(ツ)_/¯

/*

Each test was wrapped in the below, for simple benchmarking

$start = microtime(true);

// code

$end = microtime(true);

$time = $end - $start;

echo 'Time taken : ' . $time;

*/ $arr = [];

foreach (range(1,300000) as $i) {

$obj = new stdClass;

$obj->id = $i;

$obj->firstname = str_repeat('a', rand(5,10));

$obj->lastname = str_repeat('b', rand(5,10));

$arr[] = $obj;

} /* Test 1 - array_walk() */

$i = 1;

array_walk($arr, function($item, $key) use (&$i) {

$item->fullname = $item->firstname.' '.$item->lastname.$i++;

});

// Time taken : 0.057256937026978

/* Test 2 - foreach() */

$i = 1;

foreach ($arr as $item) {

$item->fullname = $item->firstname.' '.$item->lastname.$i++;

}

// Time taken : 0.043232917785645

In repeat tests, the foreach() loop performed the action an average of 33% faster than the callback function to array_walk(). After doing some research, I found some suggestions that this could be due to array_walk() defining a new function for each item passed in the array, creating more overhead in the php engine, thus slowing down the time it takes to process each item.

Straight away this got me thinking, what use is array_walk() if we can achieve that same thing much faster with the foreach() function..

One thing that sprung to mind was that methods could be defined elsewhere in the script and called instead — thus leading to a more DRY codebase.. and according to my research, PHP OPCache should precompile these functions ONCE, leading to an increase in performance. So I gave that a try, while also moving the foreach loop into a single function as well.

function createfullname(&$arr, &$i) {

foreach ($arr as $item) {

$item->fullname = $item->firstname.' '.$item->lastname.$i++;

}

} function aw_createfullname($item, $key, &$i) {

$item->fullname = $item->firstname.' '.$item->lastname.$i++;

} /* Test 1 - array_walk() */

array_walk($arr, 'aw_createfullname', $i);

// Time taken : 0.052831172943115 /* Test 2 - foreach() */

createfullname($arr, $i);

// Time taken : 0.043212890625

This led to a roughly 10% increase for the array_walk() function, but no change in the foreach() comparison (as we would expect really).

At this stage, I am pretty confident there is no real world benefit to using array_walk() over using foreach(). It doesn't provide an increase in performance, nor does it read better than a foreach loop would, so in this case, I don’t think that ill be needing to use this array function often in my day-to-day code 🤷‍♂

Feel free to suggest any reasons/scenarios where array_walk() > foreach(), I am always keen to hear them!