LaravelでLazy LoadingとEager Loadingの処理速度を比較してみた。

## はじめに
`LazyLoading`と`Eager Loading`ってどれぐらい速度が変わるんだ？と思ったので比較してみます。

## 環境

#### M1 Mac
```
ProductName:	macOS
ProductVersion:	11.6
BuildVersion:	20G165
```

#### Docker for Mac
```
Docker version 20.10.12, build e91ed57
```

#### PHP, Laravel
```
$ php --version
PHP 8.0.23 (cli) (built: Sep 13 2022 11:09:24) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.23, Copyright (c) Zend Technologies

$ php artisan --version
Laravel Framework 9.31.0
```

## テーブル
長くなっちゃうのでだいぶ端折ります。
`migration`, `factory`, `seeder`, `model`をそれぞれそれっぽいところを書きます。

### Migrations
```php
// authorsテーブルのマイグレーション定義
public function up()
{
    Schema::create('authors', function (Blueprint $table) {
        $table->id();
        $table->string('name');
    });
}

// booksテーブルのマイグレーション定義
public function up()
{
    Schema::create('books', function (Blueprint $table) {
        $table->id();
        $table->integer('author_id', false, true)->references('id')
            ->on('authors')->onUpdate('CASCADE')->onDelete('CASCADE');
        $table->string('name');
    });
}
```

### Fakers

```php
// AuthorFactory
public function definition()
{
    return [
        'name' => $this->faker->name()
    ];
}

// BookFactory
public function definition()
{
    return [
        'author_id' => Author::factory(),
        'name' => $this->faker->sentence,
    ];
}

```

### Seeders

```php
// BookSeeder
public function run()
{
    \App\Models\Book::factory(10000)->create();
}

// DatabaseSeeder
public function run()
{
    $this->call([
        BookSeeder::class,
    ]);
}
```

#### Model
```php
// BookModel リレーション
public function author(): HasOne
{
    return $this->hasOne(Author::class, 'id', 'author_id');
}
```

ターミナルでたーん！
```terminal
$ php artisan migrate --seed

  INFO  Running migrations.

2022_09_28_054159_create_books_table ............................. 30ms DONE
2022_09_28_054234_create_author_table ............................ 27ms DONE

  INFO  Seeding database.

Database\Seeders\BookSeeder ........................................ RUNNING
Database\Seeders\BookSeeder ............................... 9,115.43 ms DONE
```

## 処理時間の計測
やっと準備完了しました。さっそく遊んで行きます。

#### Lazy Loading
```api.php
// Lazy Loading
$time_start = microtime(true);
$books = Book::get();
foreach ($books as $book) {
    $book->author;
}
$time_end = microtime(true);
$lazy_time = $time_end - $time_start;
echo ('Lazy Loading : ' . $eager_time . " [sec]\n");

unset($books, $time_start, $time_start);

// Eager Loading
$time_start = microtime(true);
$books = Book::with('author')->get();
$time_end = microtime(true);
$eager_time = $time_end - $time_start;
echo ('Eager Loading : ' . $eager_time . " [sec]\n");
echo ($lazy_time / $eager_time . '倍');
```

```terminal
$ curl localhost:200/api
Lazy Loading : 5.0409960746765 [sec]
Eager Loading : 0.16714096069336 [sec]
30.16014778044 倍
```

30倍も早いんですね。

### いろいろ変えて試してみる
カラム数とかって影響するのか気になったので`authors`テーブルに１カラム追加します。
```sql
ALTER TABLE `authors` ADD COLUMN `random_string` VARCHAR(255) DEFAULT 'test' AFTER `name`;
```
誤差レベルですね・・・むしろ速くなってる。
```
$ curl localhost:200/api
Lazy Loading : 5.0331110954285 [sec]
Eager Loading : 0.16527915000916 [sec]
30.452184048319 倍
```


indexはってみたらどうかしら。
```sql
CREATE UNIQUE INDEX `index_id` ON `authors` (`id`);
CREATE UNIQUE INDEX `index_id` ON `books` (`id`);
```
誤差ですねぇ。
```
Lazy Loading : 4.9837791919708 [sec]
Eager Loading : 0.17623591423035 [sec]
28.279021411361 倍
```
本の著者が複数人でもいいじゃないということで、`一対多`リレーションに変更してみます。
変更したファイルだけ書いてます。
#### Migrations
```php
// booksテーブル
public function up()
{
    Schema::create('books', function (Blueprint $table) {
        $table->id();
        // $table->integer('author_id', false, true)->references('id')
        //    ->on('authors')->onUpdate('CASCADE')->onDelete('CASCADE');
        $table->string('name');
    });
}

// authorsテーブル
public function up()
{
    Schema::create('authors', function (Blueprint $table) {
        $table->id();
        $table->integer('book_id', false, true)->references('id')
            ->on('books')->onUpdate('CASCADE')->onDelete('CASCADE');
        $table->string('name');
    });
}
```

```Book.php
public function authors(): HasMany
{
    return $this->hasMany(Author::class, 'book_id', 'id');
}
```
```BookSeeder.php
public function run()
{
    $faker = \Faker\Factory::create();

    \App\Models\Book::factory(10000)->create()->each(function ($book) use ($faker) {
        \App\Models\Author::factory(2)->create([
            'book_id' => $book->id,
            'name' => $faker->name(),
        ]);
    });
}
```
たーん
```terminal
$ php artisan migrate:refresh

  INFO  Rolling back migrations.

  2022_09_28_054234_create_author_table .................................................................................................. 20ms DONE
  2022_09_28_054159_create_books_table ................................................................................................... 10ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ................................................................................... 6ms DONE

   INFO  Running migrations.

  2019_12_14_000001_create_personal_access_tokens_table ................................................................................. 105ms DONE
  2022_09_28_054159_create_books_table ................................................................................................... 31ms DONE
  2022_09_28_054234_create_author_table .................................................................................................. 30ms DONE

$ php artisan db:seed

   INFO  Seeding database.

  Database\Seeders\BookSeeder .............................................................................................................. RUNNING
  Database\Seeders\BookSeeder .................................................................................................... 13,367.11 ms DONE
```
さらにたーん

```terminal
$ curl localhost:200/api/
Lazy Loading : 130.49195289612 [sec]
Eager Loading : 0.20387506484985 [sec]
640.05842495854 倍
```
....ふぁっ！？ `Lazy Loading`の方は`約２６倍`も経過時間が増えているのに対して`Eager Loading`は`0.03秒`しか増えていらっしゃらないので`640倍`も速度差がでてしまいました。圧倒的じゃないか我が軍は・・・(にわか）

## 検証？おわり
ちゃんと`Eager Loading`を使わないとえらい目に合うことがよく分かりました。

## 参考
Laravelの`Eager Loading`、 `Lazy Laoding`について

https://qiita.com/shosho/items/abf6423283f761703d01

`Eager Loading`, `LazyLoading`のSQL発行回数の確認方法

https://qiita.com/lvn-hanada/private/58261499f815ba6c39e7