SplFixedArray and Friends: Memory-Efficient Data Structures
AI generated
<?php
8.4
PHP · SplFixedArray · Data Structures · Memory
SplFixedArray and Friends Instead of PHP Arrays
Memory-efficient data structures for large data volumes

An ordinary PHP array is internally an ordered hash table with considerable overhead per element, even when only sequential integer keys are used. SplFixedArray, SplStack, SplQueue and SplHeap provide specialized, memory-efficient data structures that noticeably reduce that overhead for large, clearly structured data volumes.

14 min read SplFixedArray · SplStack · SplHeap PHP 8.x

1. Why ordinary PHP arrays carry a lot of overhead per element

A PHP array is internally not a simple, contiguous memory list but an ordered hash table that must manage both arbitrary keys and the insertion order. For every single element, this structure stores, in addition to the actual value, a hash bucket entry, a pointer to the next element in insertion order, and management information for the underlying zend_array. Depending on PHP version and value type, this overhead frequently amounts to several dozen extra bytes per element, regardless of the actual payload content.

With an array of a few hundred elements, this overhead practically never gets noticed. With millions of elements, for example when importing large CSV files, processing sensor data, or building large numeric vectors for computation, this overhead instead adds up to considerable, avoidable memory usage that would not be necessary at all for a simple array with purely sequential integer values.

The Standard PHP Library, SPL for short, offers specialized, memory-efficient data structures for exactly this case: SplFixedArray for fixed size arrays with sequential integer keys, SplStack, SplQueue and SplDoublyLinkedList for sequential access patterns without random access, and SplHeap and SplPriorityQueue for sorted processing. Each of these structures deliberately forgoes functionality that an ordinary array offers but that is not needed in the concrete use case, thereby saving memory.

2. SplFixedArray: fixed size, considerably less memory

SplFixedArray is the most direct alternative to an ordinary PHP array for the case where the number of elements is known in advance and only sequential integer indices starting at zero are used. Internally, SplFixedArray entirely forgoes the hash table structure of an ordinary array and instead uses a genuine, contiguous C array memory block, in which every element sits directly at its computed memory position without hash bucket overhead.

The price for this efficiency: SplFixedArray supports neither string keys nor dynamic growth without explicit setSize() calls, and access beyond the fixed size raises an OutOfRangeException instead of silently creating a new element. For use cases with a known, fixed size in advance, for example reading a CSV file with a known row count or generating a numeric vector of a fixed length for a mathematical computation, that is not a drawback but a welcome early warning for programming mistakes.


declare(strict_types=1);

// Regular array: hash table overhead per element, even with sequential int keys
$regularArray = [];
for ($i = 0; $i < 1_000_000; $i++) {
    $regularArray[$i] = $i * 1.5;
}
printf("Regular array: %d bytes\n", memory_get_usage(true));

unset($regularArray);
gc_collect_cycles();

// SplFixedArray: contiguous memory block, no hash bucket overhead
$fixedArray = new SplFixedArray(1_000_000);
for ($i = 0; $i < 1_000_000; $i++) {
    $fixedArray[$i] = $i * 1.5;
}
printf("SplFixedArray: %d bytes\n", memory_get_usage(true));

// Accessing beyond the declared size raises OutOfRangeException,
// catching programming errors instead of silently creating new keys

3. SplStack, SplQueue and SplDoublyLinkedList in detail

SplDoublyLinkedList models a doubly linked list, where every element only stores its value plus pointers to its predecessor and successor, without the additional hash bucket structure of an ordinary array. SplStack and SplQueue inherit from this base class and specialize in last in first out and first in first out access patterns respectively, with correspondingly named methods like push(), pop(), enqueue() and dequeue() that express intent in code far more clearly than an ordinary array with array_push() and array_shift().

An important performance difference compared to an array: array_shift() on an ordinary PHP array must shift all remaining elements forward by one position, which becomes a linear time operation for large arrays. SplQueue::dequeue(), by contrast, merely removes the head of the linked list and adjusts a pointer, which happens in constant time regardless of list size. For frequent queue operations on large data volumes, this difference is not just a memory question but also a noticeable performance advantage.


declare(strict_types=1);

/**
 * Process a large batch of jobs using SplQueue instead of a plain array
 * for constant-time dequeue instead of array_shift's linear cost.
 */
final class JobQueue
{
    private SplQueue $queue;

    public function __construct()
    {
        $this->queue = new SplQueue();
    }

    public function enqueue(callable $job): void
    {
        $this->queue->enqueue($job);
    }

    public function processAll(): void
    {
        while (!$this->queue->isEmpty()) {
            $job = $this->queue->dequeue(); // O(1), unlike array_shift()
            $job();
        }
    }
}

4. SplHeap and SplPriorityQueue for sorted processing

SplHeap implements a binary heap, a tree structure that always provides constant time access to the smallest or largest element, depending on whether a derived class SplMinHeap or SplMaxHeap is used, or a custom compare() method is implemented. Compared to the naive approach of completely resorting an array on every insertion, SplHeap offers logarithmic instead of linear or even quadratic runtime for insert and extract operations.

SplPriorityQueue builds on the same heap principle but lets you insert values together with a separate priority, where extraction order is determined exclusively by that priority, not by insertion order. For use cases like a task queue with different urgency levels or a Dijkstra implementation for shortest paths, SplPriorityQueue is the obvious, memory-efficient choice over a manually sorted array that would need reordering after every insertion.

5. Practical example: bulk import with SplFixedArray

A typical use case for SplFixedArray is importing a large CSV file with a known row count, for example a product catalog export with several hundred thousand rows that should be held fully in memory before being written to a database. Instead of an ordinary array, which potentially needs to be reallocated with every [] = access, the target size is determined in advance through count() on the source file or a known metadata value, and SplFixedArray is initialized directly with this size.

This approach avoids not only the hash table overhead per row but also repeated internal reallocations that can occur with an ordinary array when its internally reserved memory block needs to be enlarged multiple times while being filled. For very large import volumes in the millions, this combination of reduced overhead and avoided reallocations makes a measurable difference in peak memory usage during the import.


declare(strict_types=1);

/**
 * Reads a CSV file with a known row count into a memory-efficient
 * fixed-size structure instead of a dynamically growing array.
 *
 * @param string $path Path to the CSV file.
 * @param int $expectedRows Known number of data rows, excluding the header.
 * @return SplFixedArray<array<int, string>>
 */
function importCsvBulk(string $path, int $expectedRows): SplFixedArray
{
    $rows = new SplFixedArray($expectedRows);
    $handle = fopen($path, 'rb');

    fgetcsv($handle); // skip header row

    $index = 0;
    while (($row = fgetcsv($handle)) !== false && $index < $expectedRows) {
        $rows[$index] = $row;
        $index++;
    }

    fclose($handle);
    return $rows;
}

6. Measuring memory usage: array versus SPL structure

To confirm the actual memory advantage in your own use case, a simple comparison with memory_get_peak_usage(true) before and after filling an ordinary array and the corresponding SPL structure with identical test data is enough. Important: call gc_collect_cycles() between the two measurements and, if possible, run each measurement in a separate PHP process, to avoid distortions from memory already occupied but not yet released by the previous test.

As a rough guideline, the memory advantage of SplFixedArray over an ordinary array with pure integer or float values often falls in the range of thirty to fifty percent less memory usage, depending on PHP version and the concrete value types stored. For arrays with more complex, nested values like objects or subarrays, the relative difference is smaller, because the memory needed for the values themselves reduces the relative share of the pure hash table overhead.

7. When switching actually pays off

Switching to SplFixedArray or another SPL structure pays off primarily with large data volumes in the six to seven digit element range, where memory usage actually becomes the limiting factor, for example in batch jobs, data exports, or numeric computations held entirely in memory. For smaller data volumes in the low three or four digit range, the absolute memory difference is usually negligible, while the reduced readability and the lack of support for familiar array functions like array_map() would unnecessarily complicate the code.

Another, often overlooked criterion: SPL structures implement Iterator and can therefore be used in foreach loops, but the rich collection of array functions like array_filter(), array_map() or array_reduce() does not work directly on them. A detour through iterator_to_array() cancels out the memory advantage again, since it internally creates an ordinary array. The switch is therefore only worthwhile if the code actually works consistently with the specialized structure, instead of using it only briefly and then converting it back to an array anyway.

8. Limits and compatibility issues of SPL structures

A practical problem when using SPL structures: many third party libraries and framework components explicitly expect an array as a type hint or return type and do not accept SplFixedArray or SplStack directly, even if these structures would fulfill the same business purpose. A conversion at the interface to such libraries then becomes unavoidable, which cancels out the memory advantage at exactly that point.

A second point concerns SplFixedArray specifically: since the size is fixed, any subsequent growth requires an explicit call to setSize(), which internally triggers a complete reallocation of the underlying memory block. If SplFixedArray is used incorrectly for data volumes of unknown, frequently growing size, repeated setSize() calls can potentially create more overhead than an ordinary array with its built in, automatic capacity growth would have caused. The fixed size is therefore a strength only when the element count is actually known in advance.

9. Data structures compared directly

A direct comparison shows which structure is the right choice for which use case.

Structure Access pattern Memory profile Recommendation
Ordinary array Arbitrary, string and integer keys High overhead per element Small to medium data volumes
SplFixedArray Sequential integers, fixed size Low, contiguous block Large volumes with known size
SplQueue / SplStack FIFO / LIFO, no random access Low, linked list Queues, constant dequeue time
SplHeap / SplPriorityQueue Sorted access to the extreme value Medium, tree structure Prioritized processing, shortest paths

The comparison makes it clear: there is no universally best structure, each SPL class specializes in a particular access pattern. The memory advantage arises precisely because each structure deliberately forgoes functionality not needed for that specific access pattern.

Mironsoft

PHP memory optimization, batch processing and data structure consulting

Want to process large data volumes more memory-efficiently?

We analyze batch jobs, import routines and numeric processing for unnecessary array overhead and migrate deliberately to SplFixedArray, SplQueue or SplHeap wherever the switch actually pays off measurably.

Memory profile analysis

Measuring the actual overhead of existing array based processing

Targeted SPL migration

Rebuilding bulk imports and queues onto memory-efficient SPL structures

Compatibility check

Checking where library interfaces force conversion back to arrays

10. Summary

Ordinary PHP arrays are internally ordered hash tables with noticeable overhead per element, regardless of whether that overhead is actually needed for the business logic. SplFixedArray, SplStack, SplQueue and SplHeap offer memory-efficient alternatives that are each specialized for their particular access pattern: SplFixedArray for data with a fixed size and sequential integer indices, SplQueue and SplStack for constant time insert and extract operations, SplHeap for sorted processing with logarithmic runtime.

The switch pays off primarily with large data volumes in the six to seven digit range, where memory usage actually becomes the limiting factor. With smaller data volumes, or frequent contact with libraries that explicitly expect ordinary arrays, the drawbacks of restricted functionality usually clearly outweigh the memory advantage.

SplFixedArray and Friends, the Key Takeaways

Arrays carry hash table overhead

Every element costs extra memory for a hash bucket and management data, regardless of value type.

SplFixedArray for fixed size

Contiguous memory block without hash overhead, ideal for a known element count.

SplQueue for constant dequeue time

Avoids the linear cost of array_shift() for large queues.

Switch only when actually needed

Pays off from six to seven digit element counts, otherwise the functionality loss outweighs it.

11. FAQ: SplFixedArray and Friends Instead of PHP Arrays

1What is SplFixedArray?
A fixed, contiguous memory structure with integer indices from zero, without hash table overhead.
2Why does an array use more memory?
It is a hash table with additional overhead per element, regardless of value type.
3How much does SplFixedArray save?
Often thirty to fifty percent for simple values, depending on PHP version and data type.
4Why is SplQueue faster?
dequeue() runs in constant time, array_shift() must shift all elements.
5Does array_map() work directly?
No, only via iterator_to_array(), which cancels out the memory advantage.
6What is SplHeap good for?
Sorted processing with logarithmic runtime, for example prioritized queues.
7What happens beyond the size?
An OutOfRangeException is raised instead of silently creating a new element.
8From what size does it pay off?
From six to seven digit element counts, otherwise the difference is negligible.
9Do libraries accept SplFixedArray?
Often not directly, many expect array explicitly and force a conversion.
10Suitable for growing data volumes?
No, every growth needs setSize() with reallocation, an array is usually better for that.