Slide 1

Slide 1 text

PHP8はISUCONへの扉を開く鍵となるか PHP Conference Japan 2020 Re:born 2020.12.12 清家史郎 1

Slide 2

Slide 2 text

自己紹介 - ID - - - 清家 史郎 GitHub:seike460 Twitter:@seike460 Work at - 株式会社 Fusic (フュージック) 技術開発本部/技術開発第一部門 - チームリーダー/エバンジェリスト/プリンシパルエンジニア Skill - PHP/Go/AWS Personal - PHPカンファレンス福岡2020 幻の実行委員長 - Fukuoka.php Organizerの一人 46fm パーソナリティ @seike460 2

Slide 3

Slide 3 text

Agenda 1. ISUCON 2. PHP8とパフォーマンス 3. Webアプリケーションとパフォーマンス 4. まとめ 3

Slide 4

Slide 4 text

01 ISUCON

Slide 5

Slide 5 text

ISUCONとは ISUCON公式Blogより お題となるWebサービスを決められたレギュレーションの中で 限界まで高速化を図るチューニングバトルそれがISUCONです Webエンジニアの甲子園といっても(多分)過言ではありません。 毎年多くのWebエンジニアが優勝を目指して集まります。 Webアプリケーションといえば我らがPHP! 5

Slide 6

Slide 6 text

ISUCON10 オンライン予選の利用言語比率 ISUCON公式Blogより もちろん僕も参加しておりPHPで予選に挑戦しました。 オンライン予選 利用言語比率 利用率の全体ランキングは以下の通りです。 Go 276組 59.0% Ruby 81組 17.3% Python 47組 10.0% Nodejs 29組 6.2% PHP 18組 3.8% Rust 8組 1.7% Perl 7組 1.5% Elixir 1組 0.2% original-ruby 1組 我らのPHPちょっと少ない… 0.2% 6

Slide 7

Slide 7 text

本選出場の33チーム ISUCON公式Blogより 本戦出場で絞ると更に少ない… 意気揚々とISUCONの話をしてるくらいなので、 Go 28組 84.8% Nodejs 2組 6.1% Rust 1組 3.0% Ruby 1組 3.0% PHP 1組 3.0% もしかしてこれは僕たちのチームなのでは…!? 残念ながら僕たちは 46位で予選落ちでした … この結果は ISUCON-PHPに 希望の芽を残しました 7

Slide 8

Slide 8 text

ISUCON 予選突破を果たしたい! 次こそISUCONの予選突破をPHPで突破したい! 決意を新たに自分自身のスキルを高めていこう! そんななか…さらなる希望が現れました… 8

Slide 9

Slide 9 text

PHP8 Released! 9

Slide 10

Slide 10 text

JIT このPHP8にパフォーマンスに焦点を当てて考えます。 単純な速度向上もあるがなによりもJITがPHP8の目玉だと考えます。 JIT(ジャストインタイム・コンパイラー) JITは中間コードの重要な部分をマシンコードに変換します。 コンパイルをバイパスするおかげで、パフォーマンスとメモリ使用状況を 大幅に改善させるでしょう。 参考:KinstaのブログPHP 8の新機能(新機能、改善点、およびJITコンパイラー) 10

Slide 11

Slide 11 text

PHPの今とこれから2020 11

Slide 12

Slide 12 text

PHP8は銀の弾丸なのか? PHP8が魔法のように、すぐにWebアプリケーションのパフォーマンスを向上させる 銀の弾丸なることはあるのでしょうか? 今回はPHP8を入れることで、僕の悲願であるISUCONの予選突破を することが出来る可能性があるのかを検証していこうと思います。 12

Slide 13

Slide 13 text

02 PHPとパフォーマンス

Slide 14

Slide 14 text

PHPパフォーマンス 今回はPHP8の可能性を感じたいので、 PHP7.3.22とPHP8の比較を行いながらパフォーマンス測定を行います 今回測定したいパフォーマンスはPHPのパフォーマンス コード品質のパフォーマンスでは無いので、 PHPパラメータのチューニングのみでの比較を行います。 14

Slide 15

Slide 15 text

検証環境構成 OS MacBook Pro (16-inch, 2019) 2.6 GHz 6コアIntel Core i7 vCPU 12コア Memory 16G 15

Slide 16

Slide 16 text

検証コード github.com/VKCOM/kphp がベンチマークに使っている検証コードをお借りしました。 KPHP is a PHP compiler. It compiles a limited subset of PHP to a native binary running faster than PHP. Walk-through: benchmarking PHP vs KPHP vs C++ PHP code: quicksort algorithm このコードを利用して、純粋なPHPの速度比較を行います。 16

Slide 17

Slide 17 text

検証コード

Slide 18

Slide 18 text

検証コード function generateArr(int $size): array { $arr = []; for ($i = 0; $i < $size; $i++) { $arr[] = (int) (rand() / (1000000000 / $size)); } return $arr; } $size = 1000000; $arr = generateArr($size); $start = microtime(true); quickSort($arr); $duration = round((microtime(true) - $start) * 1000 * 1000) / 1000; echo "Sorted $size elements in {$duration}ms\n"; echo 'memory_get_usage ->' . round(memory_get_usage() / 1024 / 1024, 2) . 'MByte' . PHP_EOL; 18

Slide 19

Slide 19 text

Xdebug+webgrind なんとなく速いではなく、計測したい プロファイラーの利用 Xdebug系の設定 xdebug.mode=profile xdebug.output_dir=/tmp/xdebug xdebug.log=/tmp/xdebug/xdebug.log webgrindが利用するディレクトリ Xdebug2系の設定) xdebug.profiler_output_dir=/tmp/xdebug 19

Slide 20

Slide 20 text

Xdebug+webgrind /** * Directory to search for trace files */ static function xdebugOutputDir() { $dir = ini_get('xdebug.profiler_output_dir'); if ($dir=='') // Ini value not defined return realpath(Webgrind_Config::$profilerDir).'/'; return realpath($dir).'/'; } 20

Slide 21

Slide 21 text

Xdebug+webgrind Graphvizを入れれば図式化も可能 こちらでプロファイリングしながら計測していこうと JITを有効にしたら… 21

Slide 22

Slide 22 text

Xdebug 突然の死 Segmentation fault… ひとまずDebugでなんとか出来ないかgdbで見るもわからず… [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Core was generated by `php index.php'. Program terminated with signal SIGSEGV, Segmentation fault. #0 ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER () at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:1917 1917 if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { [Current thread is 1 (Thread 0x7fcae7405c80 (LWP 25999))] (gdb) bt #0 ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER () at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:1917 #1 0x000000000080f3ab in execute_ex (ex=0x7fcadd413300) at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:54545 #2 0x00007fcadce8a16e in xdebug_execute_ex (execute_data=0x7fcadd4130f0) at /tmp/php-build/source/xdebug-3.0.1/src/base/base.c:802 #3 0x000000000045a0e9 in ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER () at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:1938 #4 0x000000000080f3ab in execute_ex (ex=0x7fcadd413300) at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:54545 #5 0x00007fcadce8a16e in xdebug_execute_ex (execute_data=0x7fcadd413020) at /tmp/php-build/source/xdebug-3.0.1/src/base/base.c:802 #6 0x0000000000815a00 in zend_execute (op_array=0x7fcadd473100, return_value=0x0) at /tmp/php-build/source/8.0.0/Zend/zend_vm_execute.h:58856 #7 0x00000000007b4cda in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3) at /tmp/php-build/source/8.0.0/Zend/zend.c:1680 #8 0x0000000000759a30 in php_execute_script (primary_file=primary_file@entry=0x7ffed1e21f10) at /tmp/php-build/source/8.0.0/main/main.c:2488 #9 0x00000000008384b9 in do_cli (argc=2, argv=0x303d1f0) at /tmp/php-build/source/8.0.0/sapi/cli/php_cli.c:949 #10 0x00000000004614bb in main (argc=2, argv=0x303d1f0) at /tmp/php-build/source/8.0.0/sapi/cli/php_cli.c:1336 22

Slide 23

Slide 23 text

プロファイラーにて計測 - xdebug - 動作するがJIT有効化するとSegmentation fault… datadog - Segmentation fault - blackfire - Segmentation fault - Tideways XHProf - Segmentation fault - xhprof - Segmentation fault 泣く泣くstart時にmicrotime、endにmicrotimeを取得して計測 もし動作がうまくいってる方がいらっしゃったらぜひ共有頂きたいです󰢛 23

Slide 24

Slide 24 text

検証コード github.com/VKCOM/kphp がベンチマークに使っている検証コードをお借りしました。 KPHP is a PHP compiler. It compiles a limited subset of PHP to a native binary running faster than PHP. Walk-through: benchmarking PHP vs KPHP vs C++ PHP code: quicksort algorithm このコードを利用して、純粋なPHPの速度比較を行います。 24

Slide 25

Slide 25 text

PHP7.3.22の設定 zend_extension=opcache [opcache] opcache.enable_cli=1 ※XDebugは読み込まない 25

Slide 26

Slide 26 text

PHP8.0.0の設定 zend_extension=opcache [opcache] opcache.enable_cli=1 opcache.jit=disable ※後述するが、完全にJITを無効にしている ※XDebugは読み込まない 26

Slide 27

Slide 27 text

結果 ■PHP7.3.22 Sorted 1000000 elements in 1475.64ms Sorted 1000000 elements in 1442.352ms Sorted 1000000 elements in 1453.989ms Sorted 1000000 elements in 1545.674ms Sorted 1000000 elements in 1423.958ms memory_get_usage ->32.38MByte ■PHP8.0.0 Sorted 1000000 elements in 1397.739ms Sorted 1000000 elements in 1349.728ms Sorted 1000000 elements in 1388.148ms Sorted 1000000 elements in 1342.53ms Sorted 1000000 elements in 1381.239ms memory_get_usage ->32.34MByte 27

Slide 28

Slide 28 text

PHP8は銀の弾丸ではないのか? 速度としてはスピードアップは出来ているが、自信を持っていい感じとは言えない PHP8は銀の弾丸ではなかったのか…? いいえ、その判断は時期尚早です。PHP8の真骨頂、JITを有効化します。 28

Slide 29

Slide 29 text

PHP8 JITの設定 ■opcache.jit disable: 完全に無効にする。実行時にも有効にできません。 off: 無効にしますが、実行時に有効にできます。 tracing/on: トレーシングJIT を使う。 function: 関数単位でJITを使う。 ■opcache.jit_buffer_size int コンパイル済みのJITコードを保存する共有メモリの合計サイズ。 0 を指定すると、JIT が無効になります。 29

Slide 30

Slide 30 text

PHP8.0.0 JIT の設定 zend_extension=opcache [opcache] opcache.enable_cli=1 opcache.jit=tracing opcache.jit_buffer_size=100M 30

Slide 31

Slide 31 text

結果 ■PHP8.0.0 + JIT Sorted 1000000 elements in 391.313ms Sorted 1000000 elements in 403.318ms Sorted 1000000 elements in 392.878ms Sorted 1000000 elements in 428.734ms Sorted 1000000 elements in 396.696ms 爆速! 31

Slide 32

Slide 32 text

PHP8のJITは速い 約3倍強の速度が出ます。 つまりPHP8のJITを使えばWebアプリケーションの速度は3倍速になります。 …そんなはずありませんね。 もしそんなに単純であるなら僕たちは苦労はしない。 実際には様々なボトルネックが発生します。 ボトルネックが発生する例としてストレージを利用する例を示します。 32

Slide 33

Slide 33 text

03 Webアプリケーションとパ フォーマンス

Slide 34

Slide 34 text

Webアプリケーションのパフォーマンス Webアプリケーションにおけるパフォーマンス Client -> Nginx -> PHP -> Storage(RDBなど) - PHP -> RDBに対する処理のパフォーマンスの確認 - PHP CLI にてPostgreSQLの接続を試す 34

Slide 35

Slide 35 text

検証コード

Slide 36

Slide 36 text

検証結果 ■JITあり pg_connect 1000 times in 41586.372ms pg_connect 1000 times in 43173.084ms pg_connect 1000 times in 44172.276ms ■JITなし pg_connect 1000 times in 45814.642m pg_connect 1000 times in 45438.385ms pg_connect 1000 times in 45806.33ms RDBの処理が支配的であるため、処理改善が非常に小さい DiskStorageなどが絡んでしまうとPHP8のJITの効果は極小化してしまう 36

Slide 37

Slide 37 text

Webアプリケーションのパフォーマンス Webアプリケーションにおけるパフォーマンス Client -> Nginx -> PHP -> Storage(RDBなど) - 次にNginxからのRequest/Secondを確認する - Web負荷試験ツールを実行する 37

Slide 38

Slide 38 text

Locust PythonのWeb負荷試験ツール 何秒間に何回アクセスさせるかなどを設定することができます。 応答速度であるrequests/secを計測していきます。 38

Slide 39

Slide 39 text

Nginx応答する検証コード github.com/VKCOM/kphp がベンチマークに使っている検証コードを再度お借りしました。 但し今回確認したいのはボトルネックになりうる箇所の確認になりますので、 PHPの計算量を少なくして検証します。 具体的にはquickSortする件数を1,000,000 -> 10,000件まで減らしたコードで 検証していきます。 39

Slide 40

Slide 40 text

検証JITなし ■JITなし - 451 requests/sec こちらを基準とした時、JITを有効にする以外の変更を加えず速度の変化を見ます。 40

Slide 41

Slide 41 text

検証JITあり ■JITあり - 905 requests/sec 約二倍の速度が計測されました。 PHP単体の時より速度向上は減りましたが十分な速度向上が見られます。 41

Slide 42

Slide 42 text

Webアプリケーションのパフォーマンス Webアプリケーションのパフォーマンスは一般的にバックエンド側、 特にデータが入っている永続ストレージ部分にかかります。 では今回のPHP8のJITの対応は無駄になるのでしょうか? 42

Slide 43

Slide 43 text

プログラム言語の処理が倍以上になっている 当たり前ですが、そんなことはありません。 DBの処理が遅ければその処理をPHPに任せる方法を考えれば良い 他の言語と比べてみても匹敵する速度が確保されつつあります。 43

Slide 44

Slide 44 text

言語の速度差異 Compare C++, JS, Python, Python + numba, PHP7, PHP8, and Golang in “Prime Number” calculation.より 44

Slide 45

Slide 45 text

まとめ Point 1 PHP8のJIT、これは本当に強力です。利用方法を誤らなければ確実にPHPerの武器になります。 Point 2 但しPHP8は銀の弾丸なのかと言われるとそうではない。 Webアプリケーションにおいてボトルネックは別であったり、コード上にあることも多い。 Point 3 一方でPHP8は銀の弾丸ではないが、他の言語にも対抗出来る強力な武器にはなる Point 4 ISUCON に PHPで優勝しましょう! 45

Slide 46

Slide 46 text

Thank You ご清聴いただきありがとうございました We are Hiring ! https://recruit.fusic.co.jp/