phparraysperformancespl

SplFixedArray is much slower and much more memory hungry than array, PHP8.3


Using PHP8.3.0, testing SplFixedArray, I have this sample code

<?php

declare(strict_types=1);

$res = [];
$t = microtime(true);
for ($i = 1; $i <= 1_000_000; ++$i) {
    if (1) {
        $new = new SplFixedArray(3);
        $new[0] = "foo";
        $new[1] = "bar";
        $new[2] = "baz";
        $res[] = $new;
    } else {
        $res[] = [
            0 => "foo",
            1 => "bar",
            2 => "baz",
        ];
    }
}
$t = microtime(true) - $t;
var_dump(
    [
        'memory_get_peak_usage(true) / 1024 / 1024' => memory_get_peak_usage(true) / 1024 / 1024,
        'memory_get_peak_usage() / 1024 / 1024' => memory_get_peak_usage() / 1024 / 1024,
        't' => $t,
    ]
);

and when I benchmark it with hyperfine, first using arrays and then using SplFixedArray:

$ hyperfine --warmup 10 'php spl.php'
Benchmark 1: php spl.php
  Time (mean ± σ):      52.7 ms ±   0.8 ms    [User: 43.6 ms, System: 6.0 ms]
  Range (min … max):    51.4 ms …  56.2 ms    57 runs
 
$ hyperfine --warmup 10 'php spl.php'
Benchmark 1: php spl.php
  Time (mean ± σ):     321.1 ms ±   6.1 ms    [User: 267.6 ms, System: 34.8 ms]
  Range (min … max):   315.6 ms … 335.9 ms    10 runs

the array method average 52ms, SplFixedArray averaged 321ms

And when I look at memory consumption,

$ time php spl.php 
/home/hans/projects/headless-chomium-recorder/tests/hans/spl.php:23:
array(3) {
  'memory_get_peak_usage(true) / 1024 / 1024' =>
  double(26.0078125)
  'memory_get_peak_usage() / 1024 / 1024' =>
  double(16.383125305176)
  't' =>
  double(0.047396898269653)
}

real    0m0.060s
user    0m0.038s
sys     0m0.018s
$ time php spl.php 
/home/hans/projects/headless-chomium-recorder/tests/hans/spl.php:23:
array(3) {
  'memory_get_peak_usage(true) / 1024 / 1024' =>
  double(214.00390625)
  'memory_get_peak_usage() / 1024 / 1024' =>
  double(210.30303955078)
  't' =>
  double(0.28029799461365)
}

real    0m0.321s
user    0m0.301s
sys     0m0.000s

array use 26MB RAM, SplFixedArray use 214MB RAM..

Seems that array is much better than SplFixedArray with both in memory consumption and speed,

Quoting the SplFixedArray documentation:

The advantage is that it uses less memory than a standard array.

But I see the exact opposite effect, for me SplFixedArray seems to be using ~8 times more memory, why?


Solution

  • The reason you're seeing this is because you're not actually creating new subarrays in the array example; while you are creating new arrays in the SplFixedArray example.

    When you write

    [
        0 => "foo",
        1 => "bar",
        2 => "baz",
    ];
    

    then this subarray is interned, i.e. only created only once, and then shared between all those array elements. This also explains why the memory usage is so low. If you start modifying any of these arrays, they "separate" out and create actual array instances. You'll also see the memory usage rise if you start modifying those arrays.

    For SplFixedArray, you're creating a new array in each iteration.

    TL;DR: incorrect comparison.