【PHP,Laravel】ORMと生のSQLで処理速度がどれだけ違うのか計測する。

※いきなりですが補足ですww
この記事で考慮できていない事項について、 @recordare さんから詳しくコメントしていただきました！
記事を読みつつ併せてコメントもご確認いただくとよいかと思います！
（リンク）
https://qiita.com/T_unity/items/e9ad31fe2b70952b8156#comment-50bed8ed6ef130e45eb7

## 概要

最近個人開発でLaravelを使用していて、
データの取得には基本的にORM / Eloquantを使用しています。
ORMはDBから取得したデータを簡単にオブジェクトとして扱えるようにしてくれますが

「ORMはパフォーマンスが悪い」
（要するに実行速度が遅い）

という主張をよく見かける印象があります。

私自身SQLは絶賛勉強中でまだまだ精進が必要なのですが、
とりあえずORMとSQLでざっくりどのくらいの速度差があるのかだけでも把握しておこうと思い計測を行いました。

## 計測内容

Laravelがデフォルトで持っているusersテーブルを使用して計測を行います。
やる事は単純で、<b>ユーザーデータの全件取得</b>　です。

Laravelのファクトリとseederを使ってusersテーブルにレコードを保存していき、

・1,000件
・10,000件
・100,000件

のデータを、ORMとSQLで全件取得した場合にそれぞれどのくらい時間がかかるのかを計測します。

※PHPの実行速度計測は、以下の記事を参照

https://qiita.com/HidakaRintaro/items/8893f1a36709eee3582f

### 環境

・Laravel8
・PHP8
・Docker
・Docker compose
・MySQL8.0

### マシンスペック

実行時間はマシンスペックにも左右されると思うので、私のMacのスペックも記載しておきます。

・MacBook Air (Retina, 13-inch, 2020)
・プロセッサ　1.1 GHz クアッドコアIntel Core i5
・メモリ　8 GB 3733 MHz LPDDR4X
ちなみにOSはカタリナ...

※今回の計測の目的はORMと生SQLの差分を取る事なので、絶対的な数字はあまり気にしていなくて、差分がどれくらいあるのかを確認する事が重要だと捉えています。

## コード

### 計測処理

```php
    $start = hrtime(true); // 計測開始時間

    // 実際の処理

    $end = hrtime(true); // 計測終了時間
    $nano_sec = $end - $start;
    $micro_sec = $nano_sec / 1000;
    $milli_sec = $micro_sec / 1000;
    $sec = $milli_sec / 1000;
    echo '処理時間:'. $nano_sec .'ナノ秒' . '<br>';
    echo '処理時間:'. $micro_sec .'マイクロ秒' . '<br>';
    echo '処理時間:'. $milli_sec .'ミリ秒' . '<br>';
    echo '処理時間:'. $sec .'秒' . '<br>';

    exit;
```

### Eloquantでユーザーデータ全件取得

凄くわかりやすいデータ全件取得です。

```php
// 計測処理の記述は割愛
class UserController extends Controller
{
  public function index()
  {
    User::all();
  }
}
```

### PDOでユーザーデータ全件取得

`SELECT *`で全データを取得してきて、それを`fetchAll`で、ループすれば使用可能な状態にしておきます。

ORMがよしなにやってくれているであろうDB接続から実行します。尚、get_db_data()は自前で用意している関数で、別の箇所に記述しています。

```php
// 計測処理の記述は割愛
  public function index()
  {
    try {
      $db = $this->get_db_data();
      $que = 'SELECT * FROM users';
      $stt = $db->prepare($que);
      $stt->execute();
      $all = $stt->fetchAll(PDO::FETCH_ASSOC);
    } catch(PDOException $e) {
      exit("error occured::: {$e->getMessage()}");
    }
  }
```

## 実測値

実際に計測してみたら、以下のような結果になりました。

### ユーザーデータ1,000件

```
// シードデータの作成にかかった時間
処理時間:5089485300ナノ秒
処理時間:5089485.3マイクロ秒
処理時間:5089.4853ミリ秒
処理時間:5.0894853秒
```

```
// ORM

// 1回目
処理時間:83006800ナノ秒
処理時間:83006.8マイクロ秒
処理時間:83.0068ミリ秒
処理時間:0.0830068秒

// 2回目
処理時間:88363600ナノ秒
処理時間:88363.6マイクロ秒
処理時間:88.3636ミリ秒
処理時間:0.0883636秒

// 3回目
処理時間:83092000ナノ秒
処理時間:83092マイクロ秒
処理時間:83.092ミリ秒
処理時間:0.083092秒
```

```
// PDO

// 1回目
処理時間:7362900ナノ秒
処理時間:7362.9マイクロ秒
処理時間:7.3629ミリ秒
処理時間:0.0073629秒

// 2回目
処理時間:9002400ナノ秒
処理時間:9002.4マイクロ秒
処理時間:9.0024ミリ秒
処理時間:0.0090024秒

// 3回目
処理時間:8894600ナノ秒
処理時間:8894.6マイクロ秒
処理時間:8.8946ミリ秒
処理時間:0.0088946秒

```

計測し終えたら、`php artisan migrate:fresh`でデータを空っぽにしておきます。

### ユーザーデータ10,000件

```
// シードデータの作成にかかった時間
処理時間:62597494700ナノ秒
処理時間:62597494.7マイクロ秒
処理時間:62597.4947ミリ秒
処理時間:62.5974947秒
```

```
// ORM

// 1回目
処理時間:240455600ナノ秒
処理時間:240455.6マイクロ秒
処理時間:240.4556ミリ秒
処理時間:0.2404556秒

// 2回目
処理時間:236407000ナノ秒
処理時間:236407マイクロ秒
処理時間:236.407ミリ秒
処理時間:0.236407秒

// 3回目
処理時間:223103800ナノ秒
処理時間:223103.8マイクロ秒
処理時間:223.1038ミリ秒
処理時間:0.2231038秒

```

```
// PDO

// 1回目
処理時間:50773200ナノ秒
処理時間:50773.2マイクロ秒
処理時間:50.7732ミリ秒
処理時間:0.0507732秒

// 2回目
処理時間:45762900ナノ秒
処理時間:45762.9マイクロ秒
処理時間:45.7629ミリ秒
処理時間:0.0457629秒

// 3回目
処理時間:49979100ナノ秒
処理時間:49979.1マイクロ秒
処理時間:49.9791ミリ秒
処理時間:0.0499791秒

```

計測し終えたら`php artisan migrate:fresh`。

### ユーザーデータ100,000件

```
// シードデータの作成にかかった時間
処理時間:638011828800ナノ秒
処理時間:638011828.8マイクロ秒
処理時間:638011.8288ミリ秒
処理時間:638.0118288秒
```

```
// ORM

// 1回目
処理時間:1148815100ナノ秒
処理時間:1148815.1マイクロ秒
処理時間:1148.8151ミリ秒
処理時間:1.1488151秒

// 2回目
処理時間:1083476000ナノ秒
処理時間:1083476マイクロ秒
処理時間:1083.476ミリ秒
処理時間:1.083476秒

// 3回目
処理時間:1001656600ナノ秒
処理時間:1001656.6マイクロ秒
処理時間:1001.6566ミリ秒
処理時間:1.0016566秒

```

```
// PDO

// 1回目
処理時間:358101300ナノ秒
処理時間:358101.3マイクロ秒
処理時間:358.1013ミリ秒
処理時間:0.3581013秒

// 2回目
処理時間:322209900ナノ秒
処理時間:322209.9マイクロ秒
処理時間:322.2099ミリ秒
処理時間:0.3222099秒

// 3回目
処理時間:437579300ナノ秒
処理時間:437579.3マイクロ秒
処理時間:437.5793ミリ秒
処理時間:0.4375793秒

```

## 結果

今回計測した結果を見ると、ORMの方が、PDOでSQLを実行するよりも実行時間がおおよそ一桁上がりそうな見込みを感じました。

処理にかかる時間が数百ミリ秒とかになってくると、人間の感覚的にも「ちょっと遅くね？」と感じられるくらいの領域に入ってくるような印象です。
秒（sec）単位になってくると、「明らかに遅い」ですよね。

サービスの運用を考えると、一定の規模まではORMだけで何とかなりそうな気もしますが、テーブルが大量にあって膨大なデータ量のサービスを運用するとなると、SQLのチューニングができないとサービスとして運用するのが難しいという印象でした。

SQLとORMそれぞれの優位性を理解した上で最適な選択肢を選べるようになりたいですね。
それでは。

## 蛇足

本来であれば、この後

1,000,000件（百万件）
10,000,000件（千万件）
100,000,000件（一億件）

とさらにデータ件数を増やしてみるつもりでしたが、
作成するデータ件数の桁が上がるにつれて作成にかかる時間も一桁ずつ上がったため、10万件作成した時点で諦めました。
（100万件作ろうと思ったら6000 - 7000秒くらいかかりそうな見込み）

見込み100分くらいであれば外出中とかにできない事もないですが、10万件を作成した時点でだいぶPCのファンも回っていたため、PCの発熱状態が長時間続くのはちょっと不安が。。
最近のM1 Maxとか、MacではなくWindowsであればもっと軽快に処理できるのかもですが。。
（Mac x Dockerの組み合わせは遅すぎるとよく聞くので。）

以上、蛇足でした。