今更PHP5とPHP7の違いを肌で感じてみる(配列編)

仕事で久しぶりにPHPに触れることになりそうなのでウォーミングアップのつもりで今更ながらPHP7にチャレンジ。(PHP8の足音が。。。)

昔PHP5やってたのでその辺との違いを比較してみる。
一言で比較と言っても色々な観点があるし環境とか組み方も見なきゃだけど、まあウォーミングアップなので細かいことは置いておく。

とは言え比較の観点は決めておこう。
PHP5からPHP7では色々と変わったけど、データ構造・配列構造が改善されたのとBucket構造体が変わったとの話があったので、
今回はその観点で検証しようと思う。

PHP5までは要素が数値でも連想配列として扱っていたものが、PHP7では数値の場合はいわゆる一般的な配列として扱われるようになった。
また、bucket構造体がメモリの連続確保になったことによって、連続的なアクセスに強くなった。
という事は、数値を要素とした配列のforやforeachが早くなったはず。

よし、検証開始。

### 比較観点
* 最大メモリ使用量
* 処理速度

### 比較環境
* 端末：Macbook Air
* CPU：Core i5 1.3GHz
* メモリ：4GB 1600MHz DDR3
* OS：Sierra 10.12.6

### 検証対象
* PHP5 … PHP 5.6.32 (cli) (built: Oct 27 2017 11:56:18)
* PHP7 … PHP 7.0.26 (cli) (built: Dec  2 2017 13:12:39) ( NTS )

## 検証

### 検証方法
土台としてこんな感じの検証コードを用意。
配列をループで回して検証するシンプルなものだけど、シンプルじゃないと純粋な計測にならないのでこの程度で。
良いブログがあったので参考にさせていただきました。
(参考： https://qiita.com/h13/items/a75fba76f435212a2eb3)

```php:base.php
<?php
        $baseMem = memory_get_usage();
        $startTime = microtime(true);

        for ($i = 0; $i < 100; $i++) {
                $data = range(1, 100000);
                // {***ここに後述の検証パターン処理を差し込む***}

                $data = null;
        }

        $maxMem = (memory_get_peak_usage() - $baseMem) / (1024 * 1024); // 最大メモリ使用量
        $processTime = microtime(true) - $startTime; // 処理時間

```

### 検証パターン1. foreach

```php:foreach.php
                $output = [];
                foreach ($data as $value) {
                        $output[] = $value;
                }
```

### 検証パターン2. foreachでメモリ節約

```php:foreach-eco.php
                foreach ($data as $key => $value) {
                        $output[] = $value;
                        unset($data[$key]);
                }
```

### 検証パターン3. foreachで参照渡し

```php:foreach-ref.php
                foreach ($data as &$value) {
                        $output[] = $value;
                }
```

### 検証パターン4. foreachで参照渡しでメモリ節約

```php:foreach-ref-eco.php
                foreach ($data as &$value) {
                        $output[] = $value;
                        unset($value);
                }
```

### 検証パターン5. while

```php:while.php
                $output = [];
                while (list(, $value) = each($data)) {
                        $output[] = $value;
                }
```

### 検証パターン6. whileのメモリ節約

```php:while-eco.php
                while (list($key, $value) = each($data)) {
                        $output[] = $value;
                        unset($data[$key]);
                }
```

## 検証結果
| 検証番号 | [PHP5] 最大メモリ(MB) | [PHP5] 時間(秒) | [PHP7] 最大メモリ(MB) | [PHP7] 時間(秒) | [PHP7効果] 最大メモリ(MB) | [PHP7効果] 時間(秒) |
|:---:|---:|---:|---:|---:|:---:|:---:|
| 1 | 27.96 | 4.68 |  8.008 | 0.80 | 約 1/3 | 約 1/6 |
| 2 | 28.20 | 6.08 | 12.012 | 1.37 | 約 1/2 | 約 1/4 |
| 3 | 27.95 | 4.90 | 10.297 | 0.98 | 約 1/3 | 約 1/5 |
| 4 | 27.95 | 5.63 | 10.297 | 1.00 | 約 1/3 | 約 1/6 |
| 5 | 28.20 | 12.9 | 8.008 | 3.08 | 約 1/4 | 約 1/4 |
| 6 | 28.06 | 7.22 | 8.008 | 3.56 | 約 1/4 | 約 1/2 |

まあ、言わずもがなですが圧倒的な差が出ましたね。

全体的にPHP7が圧倒的に速くなった理由は、そもそもL1キャッシュを利用できるとかコンパイル時にASTを噛ませるようになったとかも影響してるのだろうけど、その辺の実証はまたいずれ。(その頃はPHP7とPHP8の検証の方がいいかもだけど)

## おまけ
今回とは少しズレるけどPHP7ではイテレートの変数に対して内部ポインタを使わなくなってるので、PHP5でcurrent()とかreset()とか使ってる状態でPHP7にすると結果が変わってしまう。
要注意のメモ。

## おまけのおまけ
Merry X'mas!!