PHP実行形式によるパフォーマンスの違いを比較してみよう

どうも、やまもとやまです。
最近はpythonやjsの利用が増えてきているとはいえ、WordPressを中心にまだまだ広く利用されているPHP。
WebサイトでPHPを利用する場合、モジュール版やCGI版、FastCGI、FPM(これもFastCGIですが)と複数の実行形式がありますよね。
何となく、モジュール版は速くてCGIは遅い、FastCGIはそこそこ速くてFPMもそこそこ速い、という印象がありますが、実際のところどうなのでしょうか。
気になる気になる。そうだ、比較してみよう。

環境

ベンチマークをとった環境は以下のものです。

物理ハードウェア
CPU:Intel(R) Xeon(R) Silver 4309Y CPU @ 2.80GHz x2
HDD:SAS RAID10

対象の物理ハードウェア上に、KVMで仮想マシンを作成し、仮想マシンでPHP処理をさせた場合のベンチマークを実施しました。
※PHP処理としては、WordPressトップページへのアクセスとなり、Apache Benchでベンチマークをとります

仮想マシンスペックは、4コア、メモリ4GBとなります。
OSはAlmaLinux8、PHPはAppStreamの7.4を利用します。

比較対象

以下の3種類のPHP実行について比較を行います。

  1. Apache + mod_php
  2. Apache + FastCGI(mod_fcgid)
  3. Apache + php-fpm

mod_phpはスレッドセーフでないため、1についてはMPMはpreforkで、2と3はeventを使用します。
ApacheおよびPHPの各設定は大まかにあわせるため、こんな感じにしておきました。
※各方式で、100同時実行が可能な設定になっています

もし比較条件がおかしい等があればご指摘ください。

○Apache MPM設定

<IfModule mpm_prefork_module>
    StartServers        100
    MinSpareServers     100
    MaxSpareServers     100
    ServerLimit         100
    MaxClients          100
    MaxRequestsPerChild  500
</IfModule>
<IfModule mpm_event_module>
    AsyncRequestWorkerFactor 2
    ServerLimit             10
    ThreadLimit            200
    ThreadsPerChild        200
    MaxRequestWorkers     2000
    MinSpareThreads       2000
    MaxSpareThreads       2000
    MaxConnectionsPerChild   500
</IfModule>

○fcgid設定

FcgidMaxProcesses 100
FcgidMaxProcessesPerClass 100
PHP_FCGI_MAX_REQUESTS 500

○php-fpm設定

pm = static
pm.max_children = 100
pm.max_requests = 500

それぞれでphpinfoを表示して、想定通りの実行形式になっていることを確認しておきましょう。

yamamoto11-php01

yamamoto11-php02

yamamoto11-php03

想定通りになっていますね。

ベンチマーク方法

Apache Benchでベンチマークをとります。
オプションとしては、cで同時接続数、nでリクエスト数を指定し、パフォーマンスを計測します。

例として、10同時接続で100リクエストを処理させてみましょう。

$ ab -c 10 -n 100 http://198.51.100.50/
(前略)
Server Software:        Apache
Server Hostname:        198.51.100.50
Server Port:            80

Document Path:          /
Document Length:        52884 bytes

Concurrency Level:      10
Time taken for tests:   1.667 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      5309200 bytes
HTML transferred:       5288400 bytes
Requests per second:    59.97 [#/sec] (mean)
Time per request:       166.745 [ms] (mean)
Time per request:       16.675 [ms] (mean, across all concurrent requests)
Transfer rate:          3109.39 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    47  121  61.5    105     470
Waiting:       44  116  61.1    100     468
Total:         47  121  61.5    105     470

Percentage of the requests served within a certain time (ms)
  50%    105
  66%    128
  75%    158
  80%    171
  90%    193
  95%    221
  98%    250
  99%    470
 100%    470 (longest request)

この例では、100リクエストを約1.667秒ですべて実行完了し、秒間約60リクエストを処理できています。
また、100リクエスト実行したうちの失敗やエラーはなく、すべて正常に実行完了しています。

比較してみる

では比較していきます。
リクエスト数 n は1000とし、同時接続数を変更しながら実行してみました。
結果は以下の表の通りです。

1.mod_php

同時接続数 秒間リクエスト数 エラー
10 97.26 0
30 100.61 0
50 100.37 0
100 99.46 0
150 97.76 0
200 99.36 0
250 98.64 0
300 98.46 0
400 71.24 0
500 71.11 0

 

2.FastCGI(fcgid)

同時接続数 秒間リクエスト数 エラー
10 95.31 0
30 93.10 0
50 91.38 0
100 88.60 0
150 87.59 0
200 87.72 0
250 86.84 0
300 87.64 0
400 87.54 0
500 86.67 0

 

3.php-fpm

同時接続数 秒間リクエスト数 エラー
10 98.14 0
30 100.41 0
50 99.71 0
100 100.20 0
150 100.62 0
200 100.63 0
250 100.04 0
300 99.92 0
400 100.71 0
500 100.67 0

 

同時接続が少ないときはあまり差がないですが、FastCGIが若干パフォーマンスが低いようです。
また、mod_phpは同時接続が300を超えたあたりから急激にパフォーマンスが落ちています。
今回の条件下では、php-fpmに関しては想像していた以上にパフォーマンスが高く、劣化もしないようでした。
※物理ハードウェア自体の性能、Apache/PHP設定、処理するPHPの内容、Apache Bench側のオプション等、設定する条件により、ベンチマーク結果も変わってくると思います

なお、FastCGIについては仕組み上、初回実行時はパフォーマンスが上記結果よりも低くなります。

結果として・・・

予想としてはパフォーマンスではmod_phpが有利かと思っていましたが、php-fpmが想像以上に優秀な結果となりました。
また、設定を変えつつ試してみたところ、mod_phpでは同時接続数がMaxClientsより大幅に増えるとパフォーマンスが劣化するようです。
MaxClientsを増やしていくとどうしても全体の使用メモリが増えるということもあり、総合的にもevent MPM+php-fpmが有利になりそうですね。

それではまた!