The PHP Bench

PHP ORMs 2026

Comprehensive ORM benchmarks for every scenario.

Latest run 20260928011232-b9eaed5b kaiju · PHP 8.5.11 ·
14
ORM adapters
4
storage engines
16
scenarios
3
uploads

Runtime comparison

Mean time per scenario run with warmup runs excluded, sorted by runtime. Each framework does different work, so read these next to the features you need from an ORM.

Doctrine 3.6.2

doctrine/orm · mean 514 ms · 600 samples

  • sqlite-memory 129 ms
  • sqlite-file 130 ms
  • mariadb 802 ms
  • postgresql 993 ms

PHPFUIBatch 3.0.3

phpfui/orm · mean 635 ms · 600 samples

  • sqlite-memory 53.0 ms
  • sqlite-file 924 ms
  • mariadb 604 ms
  • postgresql 957 ms

CakeCached 5.3.3

cakephp/cakephp · mean 898 ms · 600 samples

  • sqlite-memory 265 ms
  • sqlite-file 310 ms
  • mariadb 1,129 ms
  • postgresql 1,887 ms

Cake 5.3.3

cakephp/cakephp · mean 906 ms · 600 samples

  • sqlite-memory 265 ms
  • sqlite-file 308 ms
  • mariadb 1,146 ms
  • postgresql 1,906 ms

Cycle 2.16.0

cycle/database · mean 983 ms · 600 samples

  • sqlite-memory 27.5 ms
  • sqlite-file 2,157 ms
  • mariadb 901 ms
  • postgresql 846 ms

ActiveRecord 2.0.6

php-patterns/activerecord · mean 1,049 ms · 450 samples

  • sqlite-memory 101 ms
  • sqlite-file 1,614 ms
  • mariadb not run
  • postgresql 1,433 ms

Atlas 2.0.1

atlas/pdo · mean 1,095 ms · 600 samples

  • sqlite-memory 36.9 ms
  • sqlite-file 2,151 ms
  • mariadb 906 ms
  • postgresql 1,287 ms

DivergenceV2 2.1.4

divergence/divergence · mean 1,127 ms · 150 samples

  • sqlite-memory not run
  • sqlite-file not run
  • mariadb 1,127 ms
  • postgresql not run

Propel2 6cb272b6

propel/propel · mean 1,156 ms · 600 samples

  • sqlite-memory 81.5 ms
  • sqlite-file 2,231 ms
  • mariadb 1,051 ms
  • postgresql 1,260 ms

PHPFUI 3.0.3

phpfui/orm · mean 1,201 ms · 600 samples

  • sqlite-memory 59.8 ms
  • sqlite-file 2,230 ms
  • mariadb 1,011 ms
  • postgresql 1,503 ms

DivergenceV3 3.2.0

divergence/divergence · mean 1,368 ms · 744 samples

  • sqlite-memory 114 ms
  • sqlite-file 2,285 ms
  • mariadb 1,260 ms
  • postgresql 1,813 ms

Yii 2.0.54

yiisoft/yii2 · mean 1,382 ms · 600 samples

  • sqlite-memory 141 ms
  • sqlite-file 2,311 ms
  • mariadb 1,288 ms
  • postgresql 1,789 ms

Eloquent 13.2.0

illuminate/database · mean 1,851 ms · 600 samples

  • sqlite-memory 321 ms
  • sqlite-file 2,528 ms
  • mariadb 2,164 ms
  • postgresql 2,390 ms

RedBean 5.7.5

gabordemooij/redbean · mean 1,904 ms · 600 samples

  • sqlite-memory 145 ms
  • sqlite-file 2,347 ms
  • mariadb 1,624 ms
  • postgresql 3,497 ms

Storage engines

Mean across every framework measured on each engine.

  • sqlite-memory 132 ms
  • mariadb 1,155 ms
  • sqlite-file 1,656 ms
  • postgresql 1,659 ms

Scenario families

Mean across every framework and engine for each row shape.

  • simple 167 ms
  • string_variable 1,025 ms
  • string_fixed 1,054 ms
  • float_fixed 1,221 ms
  • int_fixed 1,233 ms
  • canary 1,471 ms

What the workloads measure

Every framework runs the same scenarios. The row shape and the operation mix decide where each one spends its time.

simple

167 ms
baseline row shape

Shows framework overhead with very little field-mapping noise, so identity-map, query builder, and per-record object cost dominate.

canary

1,471 ms
mixed nullable and cast-heavy fields

Wide records amplify hydration, casting, dirty-checking, and serialization overhead. Frameworks with expensive attribute normalization usually spread out here.

int_fixed

1,233 ms
narrow scalar writes

A near-floor for typed persistence. If an ORM is still slow here, the cost is usually framework machinery rather than payload complexity.

float_fixed

1,221 ms
numeric conversion and precision paths

Decimal and floating-point mapping can trigger extra normalization work, especially on update verification and dirty-state comparison.

string_fixed

1,054 ms
predictable text hydration

Fixed-length text stresses text field assignment without the variability of large blobs, so column-count and hydration strategy matter more than payload churn.

string_variable

1,025 ms
application-style text records

This is the closest to a real CRUD app payload. Variable strings and notes fields widen the gap between lightweight row mappers and heavier active-record layers.

Warmup vs hot runs
Run 1 is a warmup pass. Hot-path rankings exclude it so adapter boot, metadata discovery, and first-query setup do not drown out steady-state behavior.
Insert and indexed schemas
Indexed variants make inserts more expensive because each row write updates secondary indexes. ORMs that already have heavy unit-of-work overhead get exposed harder there.
Read all vs read probes
Read All rewards efficient bulk hydration. Random and tail probes reveal per-query setup cost, identity lookup behavior, and how much work happens for tiny result sets.
Update with verification
Updates are followed by read-back verification. That means slow dirty-checking, full-row writes, cast normalization, and post-update reads all feed into the total.
Delete phase
Delete cost is not just delete. The harness verifies the row is actually gone, so frameworks with slow teardown or follow-up lookup behavior pay twice.
Total runtime
Total runtime is a whole-machine workload number: init, insert, every read shape, every update shape, verification passes, and delete.

Runs

Imported uploads from signed runners. Open a run for its machine and per-engine detail.

Run Host Status Samples Runtime Started
20260928011232-b9eaed5b kaijuPHP 8.5.11 complete 7,488of 8,320 runs 2h 29m 1savg 1,194 ms
20260831062427-083dfb94 eimuPHP 8.5.3 complete 144of 160 runs 12.384savg 86.0 ms
20260329203643-74505162 eimuPHP 8.5.3 complete 312of 312 runs 39.501savg 127 ms

Inbox

ID Status Runner Runtime File
#3 processed kaiju 2h 29m 1savg 1,194 ms results-20260928011232-b9eaed5b-default-20260928-011232.csv received
#2 processed eimu 12.384savg 86.0 ms results-20260831062427-083dfb94-v3-3-0-staged-sqlite-memory-20260831-062427.csv received
#1 processed runner-20260329222123 39.501savg 127 ms results-20260329203643-74505162-default-20260329-203643.csv received