CentOS6にPHP7を最適化コンパイルして導入する

remiレポジトリで導入したPHP7でも十分な高速化を実現できましたが、環境に合わせた最適化コンパイルを行えばさらに速くなるらしいので試してみました。

1.目的と前提条件

CentOS6で稼働しているWordPressをさらに高速化するためにPHP7をコンパイルします。

Apacheは2.2でMPMはPreforkを利用し、OpcacheとAPCu/APC互換モジュールを利用できるようにします。これはWordPressの001 Prime Strategy Translate AcceleratorプラグインをAPCuでで利用するためにはAPC互換モジュールも導入するがあるのですが、PHP7からAPCuと分離されたとのことなので別途導入する必要があるためです。

作業はすべてrootユーザで実施。事前に「yum remove php」でPHPを削除しておきます。
また色々と厄介なSELiinuxは無効化(厳密にはPermissiveモードに)しています。

2.コンパイルに必要なパッケージをインストールする

PHPコンパイルによる高性能のFastCGIウェブサーバー構築とチューニングに関するお話し(2)の「ビルド環境構築」を参考にパッケージを導入しました。

3.PHP7の最新版安定版をダウンロードする

Current Stable PHP 7.X.Xで最新のPHP7の最新版を探しダウンロードします。

# cd /root
# wget -q http://jp2.php.net/get/php-7.0.5.tar.gz/from/this/mirror -o php-7.0.5.tar.gz
# tar zxvf php-7.0.5.tar.gz
# cd php-7.0.5
4.APCu/APCu_bcをダウンロードする

OPcacheは標準で組み込まれていますが、APCuAPCu_BCはPHP7対応バージョンをサイトからダウンロードする必要があります。またapcu_bcをビルドする際に、親モジュールとなるapcuの参照パスが「apcu」以外だとエラーになってしまうため、ディレクトリ名を変更します。apcu_bcのディレクトリ名もも念のため変更しておきます。

## モジュールのソースを設置するディレクトリextに移動
# cd ext/

## APCuをダウンロードし解凍する
# wget -q https://pecl.php.net/get/apcu-5.1.3.tgz
# tar zxvf apcu-5.1.3.tgz
#
# # APCu_bcをダウンロードし解凍する
# wget -q https://pecl.php.net/get/apcu_bc-1.0.3.tgz
# tar zxvf apcu_bc-1.0.3.tgz

## apcu_bcのビルド時エラー対策
# mv apcu-5.1.3 apcu
# mv apcu_bc-1.0.3 apcu_bc
5.ビルドの準備を行う

APCu/APCu_bv拡張モジュールをPHP7のビルドに組み込み、結果を確認します。

# cd ..
# rm -rf configure
# ./buildconf --force
Forcing buildconf
Removing configure caches
rebuilding aclocal.m4
rebuilding configure
rebuilding main/php_config.h.in

## configureヘルプにapcの項目が増えていればモジュールの組み込みが完了
# ./configure --help| grep apc
  --enable-apcu           Enable APCu support
  --disable-apcu-rwlocks  Disable rwlocks in APCu
  --enable-apcu-debug     Enable APCu debugging
  --enable-apcu-clear-signal  Enable SIGUSR1 clearing handler
  --disable-apcu-mmap     Disable mmap, falls back on shm
  --enable-apcu-spinlocks        Use spinlocks before flocks
  --enable-apc           Enable APCu BC support
6.最適化オプションを追加しconfigureを実施

configureに最適化オプションを環境変数で渡して実行します。「-O2」は最適化レベル、「-march=native」は現在のCPUに特化したコードを生成するオプションです。

そして今回はconfigureにWordPressの動作に必要なモジュール設定と、APCu/APCu_bcを有効化させる「–enable-apcu」「–enable-apc」を加えて実行します。
を追加

export CFLAGS="-O2 -march=native -pipe"
export CXXFLAGS="${CFLAGS}"
./configure \
    --prefix=/usr/bin/php7 \ #PHP7のインストール先
    --with-config-file-path=/etc/php7 \ #PHP7の設定ファイル保存先
    --enable-apcu \
    --enable-apc \
    --enable-mbstring \
    --enable-zip \
    --enable-bcmath \
    --enable-pcntl \
    --enable-ftp \
    --enable-exif \
    --enable-calendar \
    --enable-sysvmsg \
    --enable-sysvsem \
    --enable-sysvshm \
    --enable-wddx \
    --with-curl \
    --with-mcrypt \
    --with-iconv \
    --with-gmp \
    --with-pspell \
    --with-gd \
    --with-jpeg-dir=/usr \
    --with-png-dir=/usr \
    --with-zlib-dir=/usr \
    --with-xpm-dir=/usr \
    --with-freetype-dir=/usr \
    --enable-gd-native-ttf \
    --enable-gd-jis-conv \
    --with-openssl \
    --with-pdo-mysql=/usr \
    --with-gettext=/usr \
    --with-zlib=/usr \
    --with-bz2=/usr \
    --with-recode=/usr \
    --with-mysqli=/usr/bin/mysql_config \
    --with-apxs2=/usr/sbin/apxs

※参考1 検証用CentOSのCPU情報

# cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 18
model           : 1
model name      : AMD A8-3820 APU with Radeon(tm) HD Graphics
stepping        : 0
cpu MHz         : 2495.462
cache size      : 1024 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 6
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow constant_tsc tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni cx16 popcnt hypervisor lahf_lm extapic abm sse4a misalignsse 3dnowprefetch osvw
bogomips        : 4990.92
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

※参考2 お名前.comVPSサーバのCPU情報

# cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
stepping        : 11
microcode       : 1
cpu MHz         : 2593.746
cache size      : 4096 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx lm constant_tsc arch_perfmon rep_good unfair_spinlock pni ssse3 cx16 hypervisor lahf_lm
bogomips        : 5187.49
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:
7.PHP7のコンパイルとインストール

configureが完了したらいよいよコンパイルです。「-j10」で並列化したコンパイルを行い、make testでコンパイルされたPHP7の動作検証を行います。この中でmake testが一番時間が掛かった処理でした。確か20分以上は掛かったはずです。
最後に一度httpdを停止してから「make install」でPHP7をサーバにインストールします。

# make -j10
# make test
# service httpd stop
# make install
8.PHP7/Opcache/APCuの初期設定を行とApcheを起動

インストール完了後、PHP/Opcache/APCu/mbstringの初期設定を/etc/php7/php.iniに書き込みます。設定内容はremiレポジトリでインストールしたPHP7のものを参考にしています。

# /etc/php7/php.ini
max_execution_time=600
memory_limit=128M
error_reporting=0
display_errors=0
log_errors=0
user_ini.filename=
realpath_cache_size=2M
cgi.check_shebang_line=0
date.timezone = "Asia/Tokyo"
zend_extension=opcache.so

[opcache]
opcache.enable=1
;opcache.enable_cli=0
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
;opcache.max_wasted_percentage=5
;opcache.use_cwd=1
;opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.revalidate_path=0
;opcache.save_comments=1
opcache.fast_shutdown=1
;opcache.enable_file_override=0
opcache.optimization_level=0xffffffff
;opcache.inherited_hack=1
;opcache.dups_fix=0
;opcache.blacklist_filename=/etc/php.d/opcache*.blacklist
;opcache.max_file_size=0
;opcache.consistency_checks=0
;opcache.force_restart_timeout=180
;opcache.error_log=
;opcache.log_verbosity_level=1
;opcache.preferred_memory_model=
;opcache.protect_memory=0
;opcache.restrict_api=
;opcache.file_cache=
;opcache.file_cache_only=0

apc.enabled = 1
apc.enable_cli=1
apc.shm_size=40M
apc.ttl=3600
apc.gc_ttl=3600
apc.mmap_file_mask=/tmp/apc.XXXXXX

;mbstring
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = UTF-8
mbstring.http_output = pass
mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII

設定を行ったところでhttpd+phpを起動し、PHP7が動作しているか確認します。

# service httpd start
httpd を起動中:                                            [  OK  ]
# /usr/bin/php7/bin/php -v
PHP 7.0.5 (cli) (built: Apr  4 2016 12:53:11) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies
9.最適化ビルドされたPHP7の速度を計ってみる

ApacheBenchを実行し最適化PHP7+OPcache+APCuの性能を試してみます。条件は以前の検証環境と同じです。

# ab -n 100 -c 10 https://localhost/
Time taken for tests:   2.629 seconds
Requests per second:    38.04 [#/sec] (mean)
Time per request:       262.877 [ms] (mean)
Time per request:       26.288 [ms] (mean, across all concurrent requests)
Transfer rate:          391.96 [Kbytes/sec] received

remiレポジトリのPHP7が30.13/sでしたので、ざっくり25~30%ほど高速化していますね。

もし10/程度しか出ていない場合はOPcacheがエラーになっているはずです。原因はおそらくSElinux。/var/log/httpd/error_logをのぞいてみるとこんなエラーが出ていると思います。

# cat /var/log/httpd/error_log | grep opcache
--省略--
Failed loading /usr/bin/php7/lib/php/extensions/no-debug-non-zts-20151012/opcach
e.so:  /usr/bin/php7/lib/php/extensions/no-debug-non-zts-20151012/opcache.so: ca
nnot restore segment prot after reloc: Permission denied
--省略--

# getenforce
Enforcing
# setenforce 0
# getenforce
Permissive
# service httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中:                                            [  OK  ]

これで解決した場合は、次のコマンドでSElinuxのラベル付けを行えば解決するはず。

# chcon -v -R -u system_u -r object_r -t textrel_shlib_t /usr/bin/php7/lib/php/extensions/no-debug-non-zts-20151012/opcache.so
changing security context of `/usr/bin/php7/lib/php/extensions/no-debug-non-zts-20151012/opcache.so'
# setenforce 1
# getenforce
Enforcing
# service httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中:                                            [  OK  ]
10.終わりに

以上で、PHP7のコンパイル手順は完了です。記事ではアッサリ説明が終わっていますが、OPcacheが効かない、APCuを入れたのに001 Prime Strategy Translate AcceleratorでAPCキャッシュモードを利用できないなど結構苦労しました。次回はremiレポジトリのPHP7と最適化されたPHP7の速度比較を予定しています。