defineを辞めてhidefを使う
追記
PHPでdefineのかわりにhidefをつかう必要はない - id:k-z-h
トラックバックに対して反応を書いた事が無いんですが、ちゃんとした内容が掲載されていたのでこちらでも追記しておきます。エントリーアップ時にdefineを辞めてhidefに切り替えることを強く推薦したつもりは全くありませんでした。確かに僕が書いた「まとめ」の項目だけを見るとそう捉えれる事もできるのでまとめの項目を「hidefの導入を検討しても良いと思います」にしました。kazuhaさんが仰られているY!社の現状予想と僕の読解力の無さの話は置いておいて(笑)、その他defineの改善効果とPECLの話はご指摘通りかなと思いました。カンファレンス当日の発表の一部を深堀したつもりだったんですが、問題の本質に誤解を与えてしまう内容を書いた事は反省します。その他の方からdefineとhidef以外でオブジェクト定数(const)でもいいじゃんという話もコメントに頂いてまして、パフォーマンスの検証とかしてないですけどその方法もありかなと思いました。
PHPカンファレンス2013に参加してきました
PHPカンファレンス2013
2013/9/14(土)に開かれたPHPカンファレンス2013に参加してきました。主催、運営、スピーカーを担当された方々、大変お疲れ様でした。全体的には大半の人が知っている基礎的な内容が多かったと思います。スピーカーの方々も本当はもっとコアな話がしたいんだろうなぁとか、でも難しい話をし始めるとみんな分からなくなるんだろうなぁと思いながら聞いていました。個人的にセキュリティ面の知識が不足しているという事もあって、以下の2タイトルがとても勉強になりました。前職の先輩方、大学の後輩も積極的にスピーカーを担当していて凄いなぁと感心していました。PHPを2年以上書いていない僕もどこかで間違いの無い知識を発表してみようかなぁと思ったり。(笑)今日は前職の先輩がPHPのhidefについてさらりと触れられていた内容について僕の方でも追加で紹介したいと思います。
defineでは無くhidefを使う
PECL :: Package :: hidef
カンファレンスの説明でdefineはスクリプト実行毎に呼ばれるので、起動時一度定数読み込みするためにPHPExtensionを使うのが良いって説明もありました。ただしExtensionは開発コストが大きいのでPECLのhidefを使う話をします。
PHPとCentOSのversion
今回僕が実行した環境です。PHPのversionは5.4.19、CentOSは6.4になります。
$ php -v PHP 5.4.19 (cli) (built: Aug 22 2013 08:03:53) Copyright (c) 1997-2013 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies $ less /etc/redhat-release CentOS release 6.4 (Final)defineが遅いという話
PHP: apc_define_constants - Manual
apc_define_constantsの項目にもdefineが遅いという記述が書いてあります。以下は引用です。ご存知のとおり、 define() は非常に遅いです。 APC を使用する主な利点はスクリプト/アプリケーションのパフォーマンスの改善なので、 大量の定数を定義する手順を合理化するために、この仕組みが提供されています。 しかし、この関数は期待通りの動作をしません。よりよい解決策として、PECL の » hidef 拡張モジュールを試してみましょう。
hidefの設定
yumでphp-pearを、peclでhidefをinstallします。またphpのiniファイルにhidefの設定を読み込む記述を追加します。
$ sudo yum install php-pear -y $ sudo pecl install hidef (略) Build process completed successfully Installing '/usr/lib64/php/modules/hidef.so' install ok: channel://pecl.php.net/hidef-0.1.13 configuration option "php_ini" is not set to php.ini location You should add "extension=hidef.so" to php.ini $ sudo vim /etc/php.d/hidef.ini [hidef] ; add 3lines extension=hidef.so hidef.ini_path=/var/php/hidef/ hidef.data_path=/var/php/hidef/hidefの設定ファイルを置く
上で設定したhidef.ini_pathに対してXXX.iniファイルを設置します。ここではphpcon.iniという名前で配置します。また設置したhidefファイルが有効化されたかどうかを実行してみます。
$ sudo vim /var/php/hidef/phpcon.ini ; int型 int PHPCON = 2013; ; string型 str PHPCONFERENCE = "いいね!"; $ php -r "echo PHPCON;" 2013 $ php -r "echo PHPCONFERENCE;" いいね!注意点としてhidefを設定するとグローバルな定数設定になるので、影響度合いを考えて設定してください。当然hidefで設定したdefineをスクリプトから設定を上書きする事は出来ません。
<?php define( 'PHPCON', 2013 ); define( 'PHPCONFERENCE', 'いいね!' ); echo PHPCON . ' ' . PHPCONFERENCE; // 実行結果 PHP Notice: Constant PHPCON already defined in /home/yuta/work/php/hidef.php on line 3 PHP Notice: Constant PHPCONFERENCE already defined in /home/yuta/work/php/hidef.php on line 4 2013 いいね!define vs hidefのscript performance
defineとhidefのscriptパフォーマンスを比較してみます。定数化した値を参照するだけのコードそれぞれをphpファイルに落とし込んでシェルを介して10000回実行してみます。
<?php define( 'HIDEF_PHPCON', 2013 ); define( 'HIDEF_PHPCONFERENCE', 'いいね!' ); $phpcon = HIDEF_PHPCON; $phpconference = HIDEF_PHPCONFERENCE;<?php $phpcon = PHPCON; $phpconference = PHPCONFERENCE;#!/bin/sh file=$1 for i in {1..10000}; do /usr/bin/php $file done;
種別 timeコマンド結果 define ./exec.sh define.php 328.66s user 332.10s system 71% cpu 15:30.03 total hidef ./exec.sh hidef.php 374.17s user 397.88s system 76% cpu 16:49.28 total あらら、2行の定数化scriptではhidefを使った方がperformanceが悪い結果になってしまいました...これは憶測ですがphpの起動を含めた実行計測だとhidefの読み込みに処理コストが掛かっているようです。次はscriptの実行コストを計測するようにphpファイルにmicrotimeを仕込みます。下のscriptを100回実行して1回の実行の平均値を取ってみるようにします。
<?php $start = microtime(true); $phpcon = PHPCON; $phpconference = PHPCONFERENCE; echo microtime(true) - $start . "\n";
種別 平均実行時間 define 0.000384sec hidef 0.000315sec そうするとhidefの方が1.2倍速い事が分かりました。
define vs hidefをapache経由のperformance
今度はapacheを経由して実行してみます。初めにapache経由でhidefを使用したい場合は/var/php以下にhidef用のiniファイルを設置すると動作しなかったので、/var/www/php/hidefというディレクトリに設定を変更しました。
$ sudo vim /etc/php.d/hidef.ini [hidef] ; add 3lines extension=hidef.so hidef.ini_path=/var/www/php/hidef/ hidef.data_path=/var/www/php/hidef//var/www/html以下に上で使用したdefine.phpとhidef.phpを設置してabスクリプトでperformanceを見てみます。
$ ab -n 10000 -c 10 http://127.0.0.1/define.php Document Path: /define.php Document Length: 17 bytes Concurrency Level: 10 Time taken for tests: 57.315 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 2100840 bytes HTML transferred: 170068 bytes Requests per second: 174.47 [#/sec] (mean) Time per request: 57.315 [ms] (mean) Time per request: 5.732 [ms] (mean, across all concurrent requests) Transfer rate: 35.80 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 23 11.4 22 152 Processing: 4 33 21.8 29 592 Waiting: 0 25 18.9 22 517 Total: 15 56 24.8 50 605$ ab -n 10000 -c 10 http://127.0.0.1/hidef.php Document Path: /hidef.php Document Length: 17 bytes Concurrency Level: 10 Time taken for tests: 48.339 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 2100840 bytes HTML transferred: 170068 bytes Requests per second: 206.87 [#/sec] (mean) Time per request: 48.339 [ms] (mean) Time per request: 4.834 [ms] (mean, across all concurrent requests) Transfer rate: 42.44 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 20 8.1 20 118 Processing: 5 27 12.9 26 319 Waiting: 0 21 11.6 20 310 Total: 5 48 13.1 45 344 Percentage of the requests served within a certain time (ms) 50% 45 66% 48 75% 50 80% 51 90% 56 95% 63 98% 76 99% 94 100% 344 (longest request)
種別 実行時間 rps define 57.315sec 174.47 hidef 48.339sec 206.87 Apacheを経由した実行の場合、hidefの方が1.18倍速い事が分かりました。
xhprofによる検証
xhprofを入れてprofile検証をしてみます。まずはpeclからのinstallとiniファイルの設定、xhprofのwebviewプログラムをDocumentRootにコピーします。更にprofileした結果を画像で出力するgraphvizをinstallします。
$ sudo pecl install xhprof-0.9.3 $ sudo vim /etc/php.d/xhprof.ini [xhprof] ; add 2lines extension=xhprof.so xhprof.output_dir=/var/www/xhprof $ sudo cp -R /usr/share/pear/xhprof_* /var/www/html $ ls /var/www/html drwxr-xr-x. 2 root root 4.0K 9月 15 20:56 2013 xhprof_html drwxr-xr-x. 4 root root 4.0K 9月 15 20:56 2013 xhprof_lib $ sudo yum --enablerepo=remi install graphviz graphviz-gd -yprofileを参照するためのコードをdefine.phpとhidef.phpに埋め込みます。以下ではhidef.phpのコードを記載します。単純にhidefのコードを呼び出す前にxhprof_enable();で開始し、xhprof_disable();でprofileの収集を辞めて、実行したidをprofileページに渡すだけです。
<?php xhprof_enable(); $phpcon = PHPCON; $phpconference = PHPCONFERENCE; $xhprof_data = xhprof_disable(); $XHPROF_ROOT = '/var/www/html/'; $XHPROF_SOURCE_NAME = 'hidef.php'; include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php"; include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php"; $xhprof_runs = new XHProfRuns_Default(); $run_id = $xhprof_runs->save_run($xhprof_data, $XHPROF_SOURCE_NAME); echo "<a href='/xhprof_html/index.php?run=$run_id&source=$XHPROF_SOURCE_NAME'>xhprof Result</a>";aタグでリンクを定義した/xhprof_html/index.phpに結果を渡すと、callgraphを見る事ができます。凄く単純な処理しか書いていないので、このケースにおいてはあまり重宝されないかもしれないですが、複雑なロジックの際にはボトルネックを特定するために見てみると良いと思います。defineの場合は実行に0.744ms、hidefは0.357msなので、倍近くhidefの方が速いという事が分かります。
種別 実行時間 define 0.744ms hidef 0.357ms
まとめ
defineとhidefの違いを意識して導入を検討しても良いと思います。※まとめの表現を少し変えました。(2013/9/19)