PHP 7のベンチマークを取るための環境構築

## 目標

PHP 5.6とPHP 7.0のパフォーマンスの違いを検証するため、他の条件は同じにした上で、PHPのバージョンだけ差し替えて実行する環境を用意します。

## 方針

VirtualBox + Vagrantで、CentOS 6の環境を作って、そこで検証します。
CentOSの`yum`コマンドの`--enablerepo`オプションを使うことで、PHPバージョンの切換を行います。

## 構築手順

### VMのセットアップ

まず、[VirtualBox](https://www.virtualbox.org/)と[Vagrant](https://www.vagrantup.com/)の最新版をインストールしてください。

VirtualBoxとVagrantをインストールしたら、適当な作業ディレクトリを作成して、以下のコマンドを実行します。

```
$ vagrant init puphpet/centos65-x6
```

これにより、現在のディレクトリに`Vagrantfile`という名前のファイルが作成されます。`Vagrantfile`を開いて、以下の行のコメントアウトを解除(行頭の`#`を削除)してください。

```
# config.vm.network "forwarded_port", guest: 80, host: 8080
```

これによって、VM上の80番ポート(HTTP)に、ホストマシンの8080番ポートからアクセスできるようになります。

次に、以下のコマンドでVMを起動します。

```
$ vagrant up
```

しばらく待つと、VMが起動するので、以下のコマンドでログインします。

```
$ vagrant ssh
```

### CentOSセットアップ

はじめにパッケージを最新版に更新しておきましょう。

```
$ sudo yum update
```

WebサーバーはApacheを入れます。インストールできたら`service`コマンドで起動します。

```
$ sudo yum install httpd
$ sudo service httpd start
```

次に、PHP 5.6、PHP 7.0をインストールするため、Remiリポジトリを追加します。

```
$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
```

まずはPHP 5.6を入れます。php-xmlは後述するSymfonyが使用します。

```
$ sudo yum install --enablerepo=php56 php php-common php-opcache php-xml
$ sudo service httpd restart
```

ここまででPHPのインストールが終わりました。次に、ベンチマーク対象となるスクリプトを作成します。ここでは、計量なベンチマークとして「hello」を表示するだけのスクリプトを、重量級のベンチマークについては、Symfonyフレームワークを使用します。

まずは、Apacheの公開ディレクトリの権限を変更しておきます。

```
$ sudo chmod -R 777 /var/www/html
```

なお、`/var/www/html`を777に設定するのは、作業を簡単に進めるためです。本番環境で推奨される設定ではありません。

次に、メモリ使用量を記録するファイルを作成し、apacheユーザが書き込みできるよう権限を設定します。

```
$ touch /tmp/memory_usage.txt
$ chmod 777 /tmp/memory_usage.txt
```

軽量なスクリプトについては、`/var/www/html/hello.php`というファイルを、以下の内容で作成します。

```php
<?php
echo 'hello';
error_log(memory_get_peak_usage() . PHP_EOL, 3, '/tmp/memory_usage.txt');
```

'hello'と表示した後に、メモリ使用量のピーク値をログファイルに出力して終了しています。
このスクリプトには、`http://127.0.0.1:8080/hello.php`というURLでアクセスできます。

Symfonyは以下の手順でインストールします。

```
$ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
$ sudo chmod a+x /usr/local/bin/symfony
$ cd /var/www/html
$ symfony new bench
```

フロントコントローラー(`web/app.php`)の末尾に、以下の行を追加します。先ほどのスクリプトと同様、メモリ使用量をログファイルに書き出します。

```
error_log(memory_get_peak_usage() . PHP_EOL, 3, '/tmp/memory_usage.txt');
```

`http://127.0.0.1:8080/bench/web`というURLにアクセスすると、Symfonyのスタートページが表示されます。


## ベンチマーク

ベンチマークは、ホストマシン(Mac)で`ab`コマンドを使用して行います。最初の1回は、キャッシュが効かないため低速になります。2回め以降の計測値はほぼ安定します。

`ab`コマンドを実行すると、以下のような結果が表示されます。

```
$ ab -k -c4 -n 100 http://127.0.0.1:8080/hello.php
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient).....done


Server Software:        Apache/2.2.15
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /hello.php
Document Length:        5 bytes

Concurrency Level:      4
Time taken for tests:   0.129 seconds
Complete requests:      100
Failed requests:        0
Keep-Alive requests:    0
Total transferred:      19700 bytes
HTML transferred:       500 bytes
Requests per second:    773.11 [#/sec] (mean)
Time per request:       5.174 [ms] (mean)
Time per request:       1.293 [ms] (mean, across all concurrent requests)
Transfer rate:          148.73 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0       4
Processing:     1    5   4.5      3      31
Waiting:        1    3   3.4      3      31
Total:          1    5   4.6      3      31

Percentage of the requests served within a certain time (ms)
  50%      3
  66%      4
  75%      6
  80%      8
  90%     11
  95%     15
  98%     18
  99%     31
 100%     31 (longest request)
```

注目すべきは、「Requests per second」と、「Time per request」です。
Request per secondは、1秒間あたりに処理可能なリクエスト数、Time per requestはリクエストを送ってからレスポンスが返ってくるまでの時間(レイテンシ)のことです。

`/tmp/memory_usage.txt`にも、以下のような数字が書き込まれているはずです。

```
384384
```

これは、メモリ使用量で、単位はバイトです。↑の場合、約375KBのメモリを使用しています。

以下が、私の手元の環境で取得した、PHP 5.6版のベンチマークです。

||Requests per second|Time per request|Memory usage|
|---|---|---|---|
|hello|806.35 [#/sec]|4.961 [ms]|216184 [byte]|
|Symfony|68.26 [#/sec]|58.600 [ms]|2595688 [byte]|

### PHP 7.0インストール

PHP 5.6の計測ができたので、5.6をアンインストールして7.0をインストールします。

```
$ sudo yum remove php php-common php-opcache php-xml
$ sudo yum install --enablerepo=remi-php70 php php-common php-opcache php-xml
$ sudo service httpd restart
```

以下が、私の手元の環境で取得した、PHP 7.0版のベンチマークです。

||Requests per second|Time per request|Memory usage|
|---|---|---|---|
|hello|756.38 [#/sec]|5.288[ms]|348056 [byte]|
|Symfony|102.10 [#/sec]|39.176 [ms]|1857400 [byte]|

## 考察

PHP 5.6と7.0のそれぞれを比較してみると、興味深いことがわかります。

* 軽量なスクリプトでは、PHP 7.0でも速度はほとんど変わらない（というか、傾向として5.6よりも遅くなる）
* 本格的なアプリケーションでは、PHP 7.0の方が高速で、メモリ使用量も少ない

【hello】

||Requests per second|Time per request|Memory usage|
|---|---|---|---|
|5.6|806.35 [#/sec]|4.961 [ms]|216184 [byte]|
|7.0|768.67 [#/sec]|5.288 [ms]|348056 [byte]|


【Symfony】

||Requests per second|Time per request|Memory usage|
|---|---|---|---|
|5.6|68.26 [#/sec]|58.600 [ms]|2595688 [byte]|
|7.0|102.10 [#/sec]|39.176 [ms]|1857400 [byte]|

速度が速いという情報はよく見ますが、メモリ使用量はどうなの？　と思ったのでベンチマーク取ってみました。