In my previous benchmark post about the splat operator the array_map function was very slow compared to foreach. I've read a great deal that the SplFixedArray is fast and memory efficient over classic PHP array. I wanted to test it with the thought that I can't seem to get out of my head lately: how to create a smart collection in PHP.

I've created simple class with 2 map method implementation. mapMap is using the array_map function and the foreachMap method is emulating the same functionality with foreach.Here is the benchmark script:

<?php

class MapCollection
{
    private $items;

    public function __construct(array $items)
    {
        $this->items = $items;
    }

    public function mapMap(callable $callable): self
    {
        return new self(array_map($callable, $this->items));
    }

    public function foreachMap(callable $callable): self
    {
        $changes = [];

        foreach ($this->items as $key => $item) {
            $changes[$key] = $callable($item);
        }

        return new self($changes);
    }
}

class SplMapCollection extends \SplFixedArray
{
    public function __construct(array $items)
    {
        parent::__construct(count($items));

        foreach ($items as $key => $item) {
            parent::offsetSet($key, $item);
        }
    }

    public function mapMap(callable $callable): self
    {
        return new self(array_map($callable, $this->toArray()));
    }

    public function foreachMap(callable $callable): self
    {
        $changes = [];

        foreach ($this->toArray() as $key => $item) {
            $changes[$key] = $callable($item);
        }

        return new self($changes);
    }
}

$input = [];

for ($i = 0; $i < 1000000; $i++) {
    $input[] = $i;
}

$callable = function (int $item) {
    return $item++;
};

$time          = microtime(true);
$memory        = memory_get_usage(true);
$mapCollection = new MapCollection($input);

writeResults('MapCollection::__construct', $time, $memory);

$time   = microtime(true);
$memory = memory_get_usage(true);
$mapMap = $mapCollection->mapMap($callable);

writeResults('MapCollection::mapMap', $time, $memory);

$time   = microtime(true);
$memory = memory_get_usage(true);
$mapMap = $mapCollection->foreachMap($callable);

writeResults('MapCollection::foreachMap', $time, $memory);

$time             = microtime(true);
$memory           = memory_get_usage(true);
$splMapCollection = new SplMapCollection($input);

writeResults('splMapCollection::__construct', $time, $memory);

$time   = microtime(true);
$memory = memory_get_usage(true);
$mapMap = $splMapCollection->mapMap($callable);

writeResults('splMapCollection::mapMap', $time, $memory);

$time   = microtime(true);
$memory = memory_get_usage(true);
$mapMap = $splMapCollection->foreachMap($callable);

writeResults('splMapCollection::foreachMap', $time, $memory);

function writeResults(string $class, float $time, float $memory): void
{
    $timeUsage = microtime(true) - $time;
    $memoryUsage = memory_get_usage(true) - $memory;
    echo "{$class} processed in {$timeUsage} ms and used {$memoryUsage} \n\n";
}

And here are the results:

MapCollection::__construct processed in 0.00011277198791504 ms and used 0 

MapCollection::mapMap processed in 1.0667450428009 ms and used 33558528 

MapCollection::foreachMap processed in 1.4232130050659 ms and used 0 

splMapCollection::__construct processed in 0.94543695449829 ms and used 16003072 

splMapCollection::mapMap processed in 2.1207430362701 ms and used -17555456 

splMapCollection::foreachMap processed in 2.3400418758392 ms and used 0 

Conclusion

array_map vs foreach

array_map was way slower in my splat operator post because it creates a new array. Foreach was just modifying the existing array. If I emulate the same functionality with foreach where a new array is created, foreach is slightly slower. In the previous post I stated that I will think twice to use array_map again. If it's used for the use case is was designed for then it's the best option.

Array vs SplFixedArray

I must be using it wrong. SplFixedArray is way slower compared to just Array. I'll stick with array until I find a good use case or learn how to use SplFixedArray properly.

Next Post Previous Post