hnwの日記

HHVM 3.3.1とPHP 5.6.2の==の違いを調べてみた


(11/15 16:15追記)本稿で指摘している違いの1件目についてバグレポを書いたところ、すぐ直すよーとのことでした。

(11/18 17:30追記)上記修正がmasterブランチに取り込まれていることを確認しました。


PHPJITコンパイラ実装であるHHVMが最近話題ですね。本家より高速というふれこみですし、Facebookが開発・実運用している実績もあるわけですから、導入を検討されている方も多いのではないでしょうか。


とはいえ、特に商用環境に導入するとなると互換性がどこまで確保できているかも重要です。現時点でも実運用に耐える程度の互換性はあるはずですが、僕は非常に保守的な人間なので、HHVMが互換性にどこまでこだわっているのかに興味があります。


今回、==演算子に絞ってHHVMとPHPの挙動を調べてみたところ、2点の違いを見つけたので紹介します。比較にはUbuntu14.04上のprebuilt版HHVM 3.3.1と自前ビルドしたPHP 5.6.2を利用しました。

浮動小数点数と16進数値文字列の比較が浮動小数点比較されない

PHPで数値と数値文字列を==で比較した場合は数値として比較されます。HHVMでも大半の場合は同じ動作になるのですが、浮動小数点数と16進数値文字列の組み合わせに限って16進文字列が0.0として比較されるようです。

<?php
var_dump(1.0 == "0x1"); // HHVM: false, PHP: true
var_dump(0.0 == "0x1"); // HHVM: true, PHP: false


これはHHVMのバグだと思われます。==処理の分岐が複雑すぎて間違えたんでしょうか。

整数の範囲を超えた10進数値文字列が浮動小数点数として数値比較される

PHPで数値文字列同士を==で比較するときは原則として数値比較されるのですが、約2年前の修正から数値比較で精度が落ちる場合には文字列比較するようになりました(参考:「PHP 5.4.4から==の挙動が一段と難しくなりました - hnwの日記」)。しかし、HHVMはこの変更に追従していないようです。


これは次のようなコードで確認できます。

<?php
var_dump("9223372036854775807"=="9223372036854775808"); // HHVM: true, PHP: false


HHVMの挙動は少し前のバージョンのPHPと同じですから、実用上はそれほど大きい問題ではないでしょう。しかし、これはPHPの全変更に追従し続けるのは無理という証拠だとも言えそうです。特にPHPの場合はChangeLogにも残らずドキュメント化もされないような細かい変更が珍しくないので、全変更を把握することさえ非現実的かもしれません。

HHVMはPHPのCソースコードをかなり利用している


僕が見つけられた違いは上記の2点だけでした。関連する仕様の複雑さを考えるとありえないくらい同じだという印象です。


この再現性の高さはHHVMの中の人が頑張った成果かもしれませんが、PHPソースコードをうまく取り込んでいるおかげもありそうです。たとえば、HHVMのhphp/runtime/base/zend-strtod.cppを見ると、PHPのZend/zend_strtod.cをベースにしているのが明らかです。ソースコードを流用してしまえば、ドキュメント化されていないような仕様であっても再現できますし、何より楽ができそうで良い手ですね。

まとめ

  • HHVMとPHPの==に違いがあるかどうか調べた
    • 2点の違いを見つけたものの、十分互換性があると言えそう
    • とはいえ、PHPの細かい修正すべてに追従できているわけでもなさそう
  • HHVMはPHPのCソースコードをかなり流用している
    • PHPとの互換性が高い理由かもしれない