# Laravel's preload curve: 123 files buy eight times what the last 1,912 do

> The opcache measurement left Laravel between two points: no preload (76 ms) and compile everything (14 ms, plus 2.3 seconds of start-up). This record draws the curve between them.

- Kind: Measurement
- Question: How far can a curated preload take Laravel, and what does each slice cost in start-up time?
- Finding: The curve is not proportional to volume. The first 1,592 files — Laravel's own framework — buy 30 ms and add 1.2 seconds to start-up. The next 1,094 Symfony files buy 9.5 ms for free. The **123 files** after that (psr, carbon) buy 15.7 ms, more than the 1,094 before them. And the last 1,912 buy 1.8 ms while adding another 1.2 seconds. So the blanket preload the earlier record measured as a ceiling is the worst point on the curve that is not the origin: stopping at 2,809 files gives 12.77 ms for 1,514 ms of start-up, while 4,721 files ask 2,691 ms to reach 10.96 ms.
- Method: The same images, the same PHP 8.4 build and the same measurement point as the opcache record — the first request a container ever serves. Curation is a **path filter** over Composer's classmap rather than a hand-picked list of classes: a hand-picked list cannot be reproduced, a filter can. The five variants nest deliberately (framework ⊂ +symfony ⊂ +psr/carbon ⊂ everything) so the curve reads as "add this much more" rather than as four unrelated points. Fresh container per variant, three repeats, median. Preload runs in php-fpm's master process, so its cost is measured as the time until the container answers with a 200; that window includes nginx's own start-up but is the same constant across all five variants.
- Metrics: Sweet spot: 2,809 files: 12.77 ms (−81%) · Its start-up cost: +1,132 ms · What the 123-file slice buys: 15.7 ms · What the last 1,912 buy: 1.8 ms
- Measured on: 2026-08-22
- Confidence: High confidence
- Status: Current
- Programme: Language & runtime
- Environment: PHP 8.4.24 NTS · opcache on · JIT off · Application Laravel, production install, classmap-authoritative · Variants path filter over the classmap · five nested slices · Repeats 3 · median reported · Hardware Apple M4 Pro · 12 cores · Docker Desktop 29.7.2 · aarch64
- Technologies: PHP, opcache, Laravel, Composer, Docker, nginx
- To reproduce: REPEATS=3 ./bench/preload-curate.sh
- Source code: https://github.com/muhammetsafak/php-framework-bench
- Raw data: https://github.com/muhammetsafak/php-framework-bench/blob/main/results/2026-08-22/preload-curate.json
- Raw data licence: https://github.com/muhammetsafak/php-framework-bench/blob/main/LICENSE
- Published: 2026-08-23
- Source: https://www.muhammetsafak.com.tr/en/research/laravel-preload-curve/
- Language: en-US
- Author: Muhammet Şafak

---
The [opcache measurement](/en/research/opcache-preload-deploy-bill/) left Laravel
between two points: no preload file of its own, a cold first request of 76 ms, and
a blanket classmap preload that reaches 14 ms while adding 2.3 seconds to php-fpm's
start-up. That record measured the blanket version as a ceiling. This one draws the
curve between the two — and shows the ceiling is a bad place to stand.

## The curve

**Cold first request against the number of files preloaded**

The five variants nest: each point adds a slice to the one before it. The horizontal axis is files compiled.

Source: bench/preload-curate.sh, median of three repeats

|  | 0 | 1,592 | 2,686 | 2,809 | 4,721 |
| --- | --- | --- | --- | --- | --- |
| cold first request | 68.37 ms | 38.01 ms | 28.48 ms | 12.77 ms | 10.96 ms |

| Slice | Compiled | Cold request | fpm ready | Marginal gain | Marginal cost |
| --- | --- | --- | --- | --- | --- |
| no preload | 0 | 68.37 ms | 382 ms | — | — |
| laravel/framework | 1,592 | 38.01 ms | 1,553 ms | −30.4 ms | +1,171 ms |
| + symfony | 2,686 | 28.48 ms | 1,538 ms | −9.5 ms | free |
| + psr, carbon | 2,809 | 12.77 ms | 1,514 ms | −15.7 ms | free |
| everything | 4,721 | 10.96 ms | 2,691 ms | −1.8 ms | +1,177 ms |

Marginal columns are against the row above. “Free” means the start-up difference sits inside measurement noise.

## Not how many, which ones

The curve's shape is not proportional to volume, and that is the finding.

**123 files buy eight times what the last 1,912 do.** The `psr` and `carbon` slice
adds 123 files and takes the cold request from 28.48 ms to 12.77 ms. The 1,912
files after it take only 1.8 ms.

The reason is date handling: Laravel's first request pulls on Carbon's loading
chain, and that chain is deeply linked — without preloading it gets resolved again
on every cold start. A framework being "heavy" is not about how many files it has;
it is about which tree the first request walks.

> **Result**
>
> Measuring preload by volume is the wrong question. The right one is which tree the
> first request walks. In Laravel most of that tree ends at 2,809 files; the
> remaining 1,912 are code the request never reaches, and preloading them only
> books time into start-up.

## "Compile everything" is the worst choice

The earlier record measured the blanket classmap as an optimistic upper bound. The
curve shows it differently: 4,721 files buy **1.8 ms** over 2,809 and cost **1,177
ms** of start-up. The last slice charges 654 milliseconds of start-up for every
millisecond it saves.

For a service deploying ten times a day that is twelve seconds of added start-up
in exchange for 1.8 ms for the first visitor. Even on steady traffic the trade is
weak.

## It does not catch Symfony

Symfony reaches **2.50 ms** with its own official file. Laravel's best point here
is 10.96 ms — four times behind, and that is with everything preloaded.

The difference is structural. Symfony's preload file is generated from its
**compiled container**, so resolving the service definitions has already happened
at build time. Laravel's container resolves at runtime, and preload does not
remove that work — it only removes class compilation. Preload saves you from
compiling PHP source; it does not save you from the application's own set-up.

> **Caveat**
>
> The filters were chosen against this application's dependency tree. In another
> Laravel application the `carbon` slice may not be decisive, and your own packages
> add a slice of their own. What this measurement leaves behind is a **method**
> rather than a list: run `bench/preload-curate.sh` with your own filters and draw
> your own curve. And "cold" here means an empty opcache, not a cold page cache — a
> single-shot run put the same `no preload` variant at 137 ms where the median of
> three is 68.37 ms.

## The file Laravel does not ship

What this leaves is not a number but a file: a preload generated with the filter
`^/vendor/(laravel\/framework|symfony|psr|nesbot|carbon)/`, giving 12.77 ms at
1.5 seconds of start-up — the thing the framework itself does not publish. The
generator is in the repository and runs against any Laravel application.
