こんにちは、2019年は早くも半分が過ぎ、後半となりました。PHPの開発は、2019年末をターゲットとするPHP 7.4の開発がいよいよ佳境を迎えています。更に、次のメジャーバージョンアップにあたるPHP 8の開発も進められています。今回のコラムでは、PHP 7.4の新機能を始めとする開発状況などを紹介します。
PHP 8へのJIT採用が決定
PHP7の次のメジャーバージョンアップとしてPHP 8の開発が行われています。リリース自体はまだ先になりそうですが、重要な機能として以前から提案されていたJIT (Just-In-Time)機能の採用が3月に行われた投票により決定しました。なお、同時に提案されていたPHP 7.4へのJITの実験的採用は否決されています。詳細は、JITに関する提案(https://wiki.php.net/rfc/jit)を参照ください。
JIT機能のメリットは、PHPスクリプトの実行速度の大幅な高速化です。以前より、Facebookが開発するHHVMエンジンでもJIT機能が採用され、大幅な高速化が実現されています。PHP自体での採用は以前から議論されていましたが、PHP 7の開発においては、主にスクリプトエンジンの最適化により大幅な高速化が実現され、JITの採用は見送られています。今回、PHP8でJIT機能がいよいよ採用となった背景には、すでにJIT機能の採用以外では大幅な性能向上が難しくなっているということと、JIT機能を採用しても現状の実行環境との互換性を十分に確保できるという判断があったものと考えられます。
現在、PHPスクリプトは専用のバイトコードに変換されてから実行されています。JIT機能を有効にすると、バイトコードの実行時に動的にネーティブコードに変換・実行されます。PHP8に採用されるJITのエンジンとしては、LuaJITプロジェクトで開発された軽量のJITエンジンDynAsmを使用することが決まっています。過去の検討では、各種コンパイラで採用されているLLVMの採用が検討されていましたが、コード生成速度が遅く、不採用となっています。
JIT機能による実行速度の高速化の度合いは、スクリプトのコード記述内容により異なります。参考として、PHPに付属するベンチマーク用スクリプトbench.phpおよびmicro_bench.phpによるテストの結果を図1および図2に示します(実行環境: Ubuntu Linux 18.04, AMD64)。PHP8+jitではPHP7に比べて、アルゴリズム系を扱うbench.phpで約4倍、シンプルな言語機能を扱うmicro_bench.phpで約2倍に実行速度が大幅に高速化されていることがわかります。
一方で、WordPressのような実用アプリケーションにおける高速化の効果はまだ少ないことが報告されており、プロファイラや最適化等の組み合わせによる更なる高速化が期待されています。
PHP 7.4の開発状況
PHPの最新版であるバージョン7では、PHP 7.3が最新版となっています。PHP 7.4はPHPバージョン7系の次のマイナーバージョンアップ版にあたります。PHP 7.4の開発は順調に進んでおり、6月13日にアルファ1版、6月26日にアルファ2版がリリースされています。7月22日にはPHP 7.4版に含まれる機能を締切るフィーチャーフリーズが計画されています。その後は、ベータ版、リリース候補版を順次リリースし、順調にいけば11月28日にPHP 7.4.0がリリースされる計画です。
PHP 7.4で追加される主な機能を以下に示します。
- OpCacheのプリロード機能
- クラスプロパティの型指定追加
- FFI (Foreign Function Interface)機能
- 配列スプレッド構文
- アロー関数
- ??= 構文
このうち、最初の2項目についてはすでに第五回のコラムで以下の代表的な機能を紹介しています。今回は、3のFFI機能について紹介します。なお、4以降については次回以降に紹介します。
FFI(Foreign Function Interface)機能
FFI(Foreign Function Interface)とは、あるプログラミング言語から他のプログラミング言語で定義・実装された関数・クラスメソッド・変数等を利用するための機能であり、LuaやPythonなどのスクリプト言語にすでに実装されています。従来、PHPの標準エクステンションであるPECLでFFIエクステンションが配布されていましたが、長らく更新されていませんでした。今回の実装は、Zend社のDmitry Stogov氏により新たに開発されたものであり、libffiの機能を使ってコンパクトに実装されています。
従来、PHPに含まれない外部の機能を利用するには、PHPエクステンションを記述する必要があったのですが、このFFIを利用することにより、インターフェイスのみ記述するだけで、比較的容易に利用できるようになります。
余談ですが、このFFIをPHP本体に含めることには反対意見があり、投票の結果、2019年1月に賛成24、反対15という比較的僅差でPHP 7.4への採用が決まりました。これは、言語仕様については2/3、それ以外の変更については50%より賛成が多ければ採用するという規則に基づく結果ですが、この後、”Abolish narrow magins”というRFPが提出・採用され、全ての変更について2/3の賛成を要するというように規則が変更されています。
FFIを利用するには、configureスクリプトに—with-ffiオプションをつけてPHPを構築します。リスト1に簡単な例を示します。
リスト1 PHP FFIで外部関数を利用する例
————————————————————————————–
<?php
$ffi = FFI::cdef(“double atan2(const double, const double);”,”libm.so.6″);
var_dump($ffi->atan2(0.1,0.0)); // float(1.5707963267949)
————————————————————————————–
まず、メソッドcdefの静的コールによりFFIオブジェクトを作成します。この際、第一引数には通常のC言語の宣言(関数、変数、構造体等)、第二引数には呼び出す機能が実装された共有ライブラリの名前を指定します。この例では、アークタンジェントを計算する数学関数atan2を指定しています。第一引数の宣言は通常C言語のヘッダファイルに宣言されるものと同等と考えて良いでしょう。また、テスト環境のUbuntu Linux 18.04上では共有ライブラリlibm.so.6に実装されていますので、第二引数に指定します。3行目に実際にatan2関数をFFIオブジェクトのメソッドとしてコールしています。atan2(0.1,0.0)の結果は正しく 1.57… と取得されます。
C言語の引数とPHPの変数の間の変換はcdefメソッドの第一引数に指定した宣言に基づき自動的に行われます。
C言語で記述された構造体や変数などを使用することも可能です。リスト2にC言語の配列を定義して利用する例を示します。
リスト2 PHP FFIで外部変数を利用する例
————————————————————————————–
<?php
$a = FFI::new(“unsigned char[1024*1024]”);
for ($i = 0; $i < count($a); $i++) {
$a[$i] = $i;
}
var_dump($a[25]); // 出力: int(25)
var_dump(FFI::sizeof($a)); // 出力: int(1048576)
————————————————————————————–
この例では、1024×1024の大きな8bit符号なし配列を定義しています。この際、newメソッドを使用します。取得されたFFI\CDataオブジェクト($a)には配列としてアクセスすることができます。8行目でFFI::sizeofで大きさを取得しています。この結果は、1048576(=1024×1024)と8bit符号なし配列として正しく定義されていることがわかります。
FFI関数は、OPCacheに実装されたプリロード機能と組み合わせて使用するよう設計されています。これは、HTTPリクエストの度にC言語ヘッダのパースを行うことによるオーバーヘッドを避けるためです。この動作は、設定ファイルphp.iniにおいてffi.enableオプションにより設定可能です。標準では以下の設定となっており、プリロードを行った関数またはCLIインターフェイスでのみFFI機能を使用することができます。
ffi.enable=preload
なお、ffi.enableにtrueを指定すると、プリロードされていない関数を含めて常にFFI機能を利用することができます。
FFIエクステンションは、インターフェイスもやや特殊で使いこなすには慣れが必要ですが、従来PHPスクリプトでは実装が難しかった機能を容易に利用できるようにしてくれるという意味で有用な機能と言えます。一例として、オープンソースの機械学習用ライブラリTensorFlowをPHPスクリプトから利用する例がgithubで公開されていますので、URLを紹介しておきます。