Post

Poor performance of Eloquent ORM in comparison to Doctrine

Poor performance of Eloquent ORM in comparison to Doctrine

In the last article, I compared two ORMs: Eloquent mostly related to Laravel, and Doctrine associated with Symfony. I presented a comparison on why reading data from the database would be much more performant using read models instead of slow ORM models with slow hydration. Read models, of course, are the best way to read data from the database, but I didn’t properly present the comparison of ORMs, which was pointed out by one user on Reddit.

Here is the link to the related article about performance and read models: https://sarvendev.com/2024/10/unlocking-orm-performance-the-essential-role-of-read-models/

Wrong assumption

Reddit comment

Thx @walden42 😉

I’m from the Doctrine world, so I assumed that when I have the following code, the most complex part is already done, and reading data from the collection is simple. It’s true for Doctrine, but not for Eloquent.

Eloquent:

1
Product::with(['category', 'supplier', 'manufacturer', 'warehouse'])->get();

Doctrine:

1
$products = $this->ormProductRepository->findAll();

So I mapped models to array and returned data as JSON on some endpoint:

Eloquent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function index(): JsonResponse
{
    $stopWatch = new Stopwatch();

    $stopWatch->start('eloquent');
    $products = Product::with(['category', 'supplier', 'manufacturer', 'warehouse'])->get();

    $products = $products->map(static function (Product $product): array {
        return [
            'name' => $product->name,
            'price' => $product->price,
            'category_name' => $product->category->name,
            'manufacturer_name' => $product->manufacturer->name,
            'supplier_name' => $product->supplier->name,
            'warehouse_location' => $product->warehouse->location,
        ];
    });
    $event = $stopWatch->stop('eloquent');

    return new JsonResponse([
        'time' => (string) $event,
        'products' => $products,
    ]);
}

Doctrine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function index(): JsonResponse
{
    $stopWatch = new Stopwatch();

    $stopWatch->start('orm');
    $products = $this->ormProductRepository->findAll();
    $products = array_map(static function (Product $product): array {
        return [
            'name' => $product->name,
            'price' => $product->price,
            'category_name' => $product->category->name,
            'manufacturer_name' => $product->manufacturer->name,
            'supplier_name' => $product->supplier->name,
            'warehouse_location' => $product->warehouse->location,
        ];
    }, $products);
    $event = $stopWatch->stop('orm');

    return new JsonResponse([
        'time' => (string) $event,
        'products' => $products,
    ]);
}

Blackfire profiles:

Eloquent: Eloquent profile

Doctrine: Doctrine profile

As we can see in the case of Doctrine, after the slow hydration process, the data are already in the objects, so we can easily map them to the array. After hydration in Eloquent, there are still some steps required to get the data 🤯

Eloquent hydration

I thought that Eloquent would be faster because it’s based on the simpler pattern Active Record. Doctrine is more complicated because it’s based on the Data mapper pattern. And here is another comparison in the tool AB:

Eloquent:

1
2
3
4
Time taken for tests:   50.842 seconds
Complete requests:      1000
Requests per second:    19.67 [#/sec] (mean)
Time per request:       50.842 [ms] (mean)

Doctrine:

1
2
3
4
Time taken for tests:   39.943 seconds
Complete requests:      1000
Requests per second:    25.04 [#/sec] (mean)
Time per request:       39.943 [ms] (mean)

On average Doctrine is 10 ms faster per request.

Conclusion

  • the best way is to use read models especially if you read a lot of data from the database: Read more
  • keep in mind that reading data will be slower in Eloquent than in Doctrine if you use heavy ORM models to read data
Unit Testing Tips - Kamil Ruczyński
This post is licensed under CC BY 4.0 by the author.