📁 Volume II: Laravel Performance Tuning Kit
📐 Topic 22: SplFixedArray
The PHP array lie — why normal arrays are actually hash maps.
"PHP arrays are not arrays. They are hash maps.
Every key-value pair consumes 5-10x more memory than a real C array.
If you know the size of your array in advance, use SplFixedArray.
It saves 70-80% memory and is slightly faster."
⚠️ THE ARRAY LIE
Most PHP developers believe that [] is a lightweight array. In reality, PHP arrays are hash tables with significant overhead: bucket structures, hash values, pointer arrays, and memory alignment. SplFixedArray is a true C array — contiguous memory blocks with minimal overhead.
🔍 What's Wrong With Normal PHP Arrays?
PHP ARRAY (Hash Table) INTERNAL STRUCTURE
═══════════════════════════════════════════════════════════════════
When you create: $array = [1, 2, 3, 4, 5];
PHP actually allocates:
┌─────────────────────────────────────────────────────────────────┐
│ Hash Table Structure: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Bucket Array (size = power of 2, e.g., 8) │ │
│ │ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │ │
│ │ │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ │ │
│ │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Each Bucket contains: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ • key (string or int) - 24 bytes │ │
│ │ • value (zval) - 16 bytes │ │
│ │ • hash value - 8 bytes │ │
│ │ • pointer to next bucket - 8 bytes │ │
│ │ • memory alignment padding - ~8 bytes │ │
│ │ Total per element: ~64-80 bytes │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
For 100,000 elements: ~8MB of overhead!
SPLFIXEDARRAY (True C Array)
═══════════════════════════════════════════════════════════════════
When you create: $array = new SplFixedArray(100000);
PHP allocates:
┌─────────────────────────────────────────────────────────────────┐
│ Contiguous memory block: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [0][1][2][3][4][5][6][7][8][9]...[99999] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Each element: just the value (16 bytes for zval) │
│ No bucket structures. No hash values. No pointer chains. │
│ │
│ Total per element: ~16-24 bytes │
└─────────────────────────────────────────────────────────────────┘
For 100,000 elements: ~2MB of memory (75% less!)
THE DIFFERENCE
PHP arrays are optimized for flexibility (associative arrays, dynamic resizing). SplFixedArray is optimized for memory efficiency and speed (fixed size, integer keys).
📊 Memory Comparison: PHP Array vs SplFixedArray
| Number of Elements |
PHP Array Memory |
SplFixedArray Memory |
Saving |
| 1,000
| ~0.5 MB
| ~0.1 MB
| 80%
|
| 10,000
| ~5 MB
| ~1 MB
| 80%
|
| 100,000
| ~50 MB
| ~10 MB
| 80%
|
| 1,000,000
| ~500 MB
| ~100 MB
| 80%
|
| 10,000,000
| ~5 GB (crashes)
| ~1 GB
| 80% (server survives)
|
REAL-WORLD EXAMPLE
Processing a 1,000,000-row export:
- PHP array: 500MB memory → Server crashes on small instances
- SplFixedArray: 100MB memory → Works fine
- 80% memory reduction means you can handle 5x larger datasets
🔧 Using SplFixedArray
BASIC USAGE
// Create with known size
$array = new SplFixedArray(10000);
// Set values (index must be within size)
for ($i = 0; $i < 10000; $i++) {
$array[$i] = $i * 2;
}
// Get values
$value = $array[500]; // O(1) - direct memory access
// Get size
$size = $array->getSize(); // 10000
// Convert to PHP array (when you need flexibility)
$normalArray = $array->toArray();
// Convert from PHP array
$fixedArray = SplFixedArray::fromArray($normalArray);
// Resize (grow or shrink)
$array->setSize(20000); // Grows, new elements are NULL
// Iterate
foreach ($array as $key => $value) {
echo "$key: $value\n";
}
LIMITATIONS
- Integer keys only — No string keys (use normal array for associative)
- Fixed size — Resizing is possible but expensive (creates new array)
- Sequential indices — Best when keys are 0, 1, 2, 3...
- No built-in functions — array_map, array_filter won't work (convert toArray first)
⚡ Performance Comparison: Read/Write Speed
| Operation |
PHP Array |
SplFixedArray |
Difference |
| Write 1M elements
| ~0.15 sec
| ~0.12 sec
| 20% faster
|
| Read 1M elements (sequential)
| ~0.08 sec
| ~0.05 sec
| 37% faster
|
| Read 1M elements (random)
| ~0.10 sec
| ~0.06 sec
| 40% faster
|
THE BOTTOM LINE
SplFixedArray is 20-40% faster and uses 70-80% less memory than normal PHP arrays for large, fixed-size, integer-keyed datasets.
🌍 Real-World Use Cases for SplFixedArray
USE SPLFIXEDARRAY WHEN:
- Matrix operations — Grids, tables, mathematical matrices
- Data processing pipelines — Fixed-size buffers
- Caching numeric IDs — User IDs, product IDs in batches
- Lookup tables — Precomputed values by index
- Export/Import large datasets — CSV processing with known row count
- Time series data — 24 hours × 60 minutes = 1440 fixed slots
USE NORMAL ARRAYS WHEN:
- Associative arrays — String keys needed
- Dynamic resizing — Unknown final size
- Sparse arrays — Large gaps between indices
- Small datasets (< 1,000 elements — overhead negligible)
- Using array functions — array_map, array_filter, array_merge
📝 Example: Processing Large CSV Files
BEFORE: PHP ARRAY (MEMORY HEAVY)
function processLargeCsv($filename)
{
$rows = [];
$handle = fopen($filename, 'r');
while (($row = fgetcsv($handle)) !== false) {
$rows[] = $row; // 10M rows = 5GB memory
}
fclose($handle);
foreach ($rows as $row) {
// Process row
}
}
AFTER: SPLFIXEDARRAY (MEMORY EFFICIENT)
function processLargeCsv($filename)
{
// First pass: count rows
$lineCount = 0;
$handle = fopen($filename, 'r');
while (fgets($handle) !== false) $lineCount++;
fclose($handle);
// Allocate exact size
$rows = new SplFixedArray($lineCount);
// Second pass: store rows
$handle = fopen($filename, 'r');
$index = 0;
while (($row = fgetcsv($handle)) !== false) {
$rows[$index] = $row;
$index++;
}
fclose($handle);
// Process
foreach ($rows as $row) {
// Process row
}
// Memory: 1GB instead of 5GB for 10M rows!
}
MEMORY SAVINGS
For a 10,000,000 row CSV file:
- Normal array: ~5GB RAM → Crashes most servers
- SplFixedArray: ~1GB RAM → Runs on large instances
- Streaming (cursor): ~5MB RAM → Best approach!
Note: For truly massive files, streaming is better than storing in memory at all. Use SplFixedArray when you must store all rows in memory.
🔄 Converting Between PHP Arrays and SplFixedArray
// PHP array to SplFixedArray
$normalArray = [1, 2, 3, 4, 5];
$fixedArray = SplFixedArray::fromArray($normalArray);
// $fixedArray[0] = 1, $fixedArray[1] = 2, etc.
// SplFixedArray to PHP array
$fixedArray = new SplFixedArray(5);
$fixedArray[0] = 'a';
$fixedArray[1] = 'b';
$fixedArray[2] = 'c';
$fixedArray[3] = 'd';
$fixedArray[4] = 'e';
$normalArray = $fixedArray->toArray();
// $normalArray = ['a', 'b', 'c', 'd', 'e']
// Convert associative array? Can't (SplFixedArray requires integer keys)
$assoc = ['name' => 'John', 'age' => 30];
// $fixed = SplFixedArray::fromArray($assoc); // WARNING: String keys become 0, 1!
// Better: Use SplFixedArray for sequential data only
⚠️ WARNING: STRING KEYS
SplFixedArray::fromArray(['a' => 1, 'b' => 2]) will convert string keys to numeric indices (0, 1). The original keys are lost. Only use SplFixedArray for sequentially indexed arrays.
❌ When NOT to Use SplFixedArray
AVOID SPLFIXEDARRAY WHEN:
- Small arrays (< 1,000 elements) — Overhead of object creation outweighs benefits
- Associative arrays — Use normal arrays or custom objects
- Unknown size — Frequent resizing is expensive
- Using array functions — No direct support for array_map, array_filter, etc.
- JSON serialization — SplFixedArray doesn't serialize to JSON like normal arrays
- Dynamic growth — Normal arrays are optimized for growth
📌 THE RULE: Use SplFixedArray when you need to store large (100k+), fixed-size, sequentially indexed datasets. For everything else, normal arrays are fine.
📝 Topic 22 Summary: SplFixedArray
| Feature |
PHP Array |
SplFixedArray |
| Memory per 1M integers
| ~500 MB
| ~100 MB
|
Read speed (1M random)
| ~0.10 sec
| ~0.06 sec
| Write speed (1M)
| ~0.15 sec
| ~0.12 sec
|
| String keys allowed?
| Yes
| No
|
| Dynamic resizing
| Yes (fast)
| Yes (slow, recreate)
|
| array_* functions
| Yes
| No (convert first)
|
📌 THE RULE: For large, fixed-size, integer-keyed datasets, SplFixedArray saves 70-80% memory and is 20-40% faster than normal PHP arrays. For small datasets or associative arrays, stick with normal arrays.
NEXT TOPIC PREVIEW
Topic 23: Lazy Resolution for Services — Don't pay for services you don't use. How to defer service instantiation until actually needed. Save memory and CPU on every request.