Skip to content
Performance

50× faster hydration and 60× faster serialization than the field-leading PHP data-object library — without changing how you write DTOs.

Benchmarked against the most popular full-featured data-object library in the PHP/Laravel ecosystem — identical DTO shapes and attributes, inside a fully booted Laravel app with both libraries' caches warmed, 20,000 iterations per scenario after a 2,000-iteration warmup, PHP 8.4. Medians of 7 runs (5 for XML). Absolute numbers vary with hardware; the ratios stay stable across runs.

51×
Hydration throughput
faster on a flat DTO
60×
Serialization throughput
faster on a flat DTO
12×
Process memory, 52 MB XML feed
less than a SimpleXML loop

Throughput

higher is better · each scenario scaled to its own leader
Hydration — flat DTO51× faster
Simple Data Objects6.3M ops/s
Popular alternative125K ops/s
Hydration — nested DTO36× faster
Simple Data Objects3.4M ops/s
Popular alternative93K ops/s
Hydration — collection of 2021× faster
Simple Data Objects211K ops/s
Popular alternative10K ops/s
Hydration — with a date cast12× faster
Simple Data Objects1.4M ops/s
Popular alternative118K ops/s
Serialization — flat DTO60× faster
Simple Data Objects14.5M ops/s
Popular alternative241K ops/s
Serialization — nested DTO48× faster
Simple Data Objects7.7M ops/s
Popular alternative162K ops/s
Serialization — collection of 2015× faster
Simple Data Objects415K ops/s
Popular alternative27K ops/s

Streaming a 100,000-row CSV import

higher is better

Rows from a generator, hydrated one by one. Both libraries stream here and hold the same flat 12 KB — the difference is speed, not memory.

lazyCollection() — rows hydrated per second85% faster
Simple Data Objects67K rows/s
Popular alternative36K rows/s

Streaming XML — 100,000 elements, 52 MB file

each scenario in its own process

The alternative has no XML support, so throughput is measured against what a consumer writes by hand: an XMLReader loop mapping each element to an array. That loop stays as flat on memory as lazyXml() — at a fifth of the speed. Memory is compared with SimpleXML twice: a careful loop that handles one element at a time and accumulates nothing, and the heaviest common pattern, collecting every row into an array before hydrating. The same app sitting idle is at 54 MB.

Throughput — lazyXml() vs hand-written XMLReader loop5× faster
Simple Data Objects80K nodes/s
Popular alternative15K nodes/s
Peak process memory — lazyXml() vs a SimpleXML loop12× less memory
lazyXml()55 MB
SimpleXML loop692 MB
Peak process memory — lazyXml() vs SimpleXML with every row collected first18× less memory
lazyXml()55 MB
SimpleXML, collected997 MB

CPU time per operation follows the same ratios — less CPU burned per request means more headroom per server. The advantage is largest where the object itself is cheap and shrinks as real work (a date cast, twenty nested objects) takes a bigger share of each call. The from()/toArray() hot paths execute compiled per-class closures, and lazyCollection() keeps peak memory flat on any dataset size. lazyXml() does the same straight from a file: only the fields the DTO declares are read, so a 100,000-element document adds about 1 MB to the process. The SimpleXML figure is process memory (RSS), not the PHP heap — libxml builds the whole document outside PHP's memory manager, so memory_get_peak_usage() reports roughly 10 MB for lazyXml() and for the SimpleXML loop alike; only the collected variant shows up in the heap, at about 420 MB.

The numbers

ScenarioSimple Data ObjectsPopular alternativeAdvantage
Hydration — flat DTO~6,300,000 ops/s~125,000 ops/s~51×
Hydration — nested DTO~3,400,000 ops/s~93,000 ops/s~36×
Hydration — collection of 20~211,000 ops/s~10,200 ops/s~21×
Hydration — with a date cast~1,400,000 ops/s~118,000 ops/s~12×
Serialization — flat DTO~14,500,000 ops/s~241,000 ops/s~60×
Serialization — nested DTO~7,700,000 ops/s~162,000 ops/s~48×
Serialization — collection of 20~415,000 ops/s~27,500 ops/s~15×
CSV — 100,000 rows, streamed~67,000 rows/s~36,000 rows/s~1.85×
CSV — peak memory while streaming12 KB12 KBequal
XML — 100,000 elements, streamed~80,000 nodes/s~15,000 nodes/s~5×
XML — peak process memory vs a SimpleXML loop55 MB692 MB (SimpleXML)~12×
XML — peak process memory vs SimpleXML, all rows collected55 MB997 MB (SimpleXML)~18×

Don't take these numbers on faith — run them

Every figure on this page comes from a public, runnable benchmark project: identical DTO shapes for both libraries, inside a booted Laravel app, with each library's cache warmed first. Clone it, read the code, swap in your own payloads. Your absolute numbers will differ with hardware — the ratios are what to compare.

Open the benchmark repository →
git clone https://github.com/std-out/simple-data-objects-benchmark
cd simple-data-objects-benchmark
make bench       # everything, in Docker (PHP 8.4)
make bench-xml   # only the XML feed comparison

Released under the MIT License.