PHPerKaigi 2019 の「PHPでJVMに入門する」を聞いて
TL;DR
トーク「PHPでJVMに入門する」
@m3m0r7さんのPHPでJVMに入門するという内容がおもしろかった。PHPでJavaを動かすという力強いトーク。
自分のトーク時間がちょうど裏で当日現場にて聞けなったので、YouTubeで公開されている動画を拝聴しました。
PHPでバイトコードを読む方法など、いろいろPHPのことを知らなかったので、今回知らなかったことを調べてみます。
個人的な調べごと
PHPのfloat・doubleについて
トーク内で、PHPのfloat・doubleについて一瞬だけ触れていらっしゃたのあらためて調べてみる。
とりあえず、PHPの公式ドキュメント「浮動小数点数」をこちらに。
このページには、「浮動小数点数の精度」という警告があるんですね。
浮動小数点数の精度は有限です。 システムに依存しますが、PHP は通常 IEEE 754 倍精度フォーマットを使います。 この形式は、1.11e-16 のオーダーでの丸め処理で誤差が発生します。 複雑な算術演算をすると、誤差はさらに大きくなるでしょう。そしてもちろん、 いくつかの演算を組み合わせる場合にも誤差を考慮しなければなりません。
あぁ、よく聞くこのトラップって公式の警告としても掲載されているんですね。
IEEE 754 倍精度フォーマットは、IEEE Standard for Floating-Point Arithmetic、直訳すると「浮動小数点数算術標準」で、浮動小数点の計算で広く採用されている標準規格ですね。
よって、複雑な演算をすると誤差が大きくなる例として、こんな感じですね。
<?php $iv = 0; $fv = 0.0; for ($i = 0; $i < 4000000; $i++) { $r = mt_rand(1, 1000); $iv += $r; $fv += $r / 10; } var_dump($iv); var_dump($fv); // int(2001061191) // float(200106119.09999) // ちょっと値がずれてる
さらに、十進数では正確な小数で表せる有理数、たとえば 0.1 や 0.7 は、 二進数の浮動小数点数としては正確に表現できません。 これは、仮数部をいくら大きくしても同じです。 したがって、それを内部的な二進数表現に変換する際には、どうしても多少精度が落ちてしまいます。
公式ドキュメント内の例を借りるとこういうことですね。
<?php // 0.1 + 0.7 = 0.8 に見える var_dump((0.1+0.7)*10); // 0.1 + 0.7 = 7.9999999999999991118... // これを切り捨てると7になる var_dump(floor((0.1+0.7)*10));
精度の高い計算が必要な場合は、任意数学関数: BC Math関数・gmp関数を使用するとのこと。トーク内でもgmp関数は紹介されていましたね。
きっと、トーク内で言及されていたfloat・doubleの問題点はもっと深いところにあるかもしれませんが、基礎的なところを抑え直してみました。
refs
- https://floating-point-gui.de/
- https://www.php.net/manual/ja/language.types.float.php
- https://blog.takekoshi.net/float-double-tsukaenai/
バイナリリーディング
トーク中のスライドでバイナリを表示する際のコマンドに xxd
を使っていて、「それなんだっけ」って思ったので基礎的なことだろうなと自分を恥じつつ調べます。
16進数dumpするlinuxコマンドですね。
$ man xdd NAME xxd - make a hexdump or do the reverse. SYNOPSIS xxd -h[elp] xxd [options] [infile [outfile]] xxd -r[evert] [options] [infile [outfile]]
雑に手元にあるGoファイルをxxdでdumpしてみます。
$ xxd main.go 00000000: 7061 636b 6167 6520 6d61 696e 0a0a 696d package main..im 00000010: 706f 7274 2028 0a09 2267 6974 6875 622e port (.."github.
バイナリレベルでの調査をしたいときに便利そうだなとおもいました。エミューレータ自作を試みたらこういうシーンに出会うのでしょうか。
refs
- https://qiita.com/rsooo/items/bb91071685f447ce29db
- http://www.tutorialspoint.com/unix_commands/xxd.htm
vld extension
PHPでJavaを読むことで、PHPの中間コードが理解しやすくなるという話がありました。vld extensionの気持ちになれるということなんですが、「なるほど、vld extensionな(知らない)」という自分でしたので調べます。Vulcan Logic Disassemblerの略でvldとのことで、PECLで配布されています。
Provides functionality to dump the internal representation of PHP scripts
PHPスクリプトの内部表現をダンプする機能を提供してくれる拡張ということですね。
オペコードを覗いてみる記事で、@syossan27さんが、入門記事を書いてくださっていたので、ありがたく引用させていただきます。
<?php echo "Hello, PHP.";
の場合は、こんな感じでオペコードをダンプできるようですね。
$ php -d vld.active=1 -d vld.execute=0 test.php Finding entry points Branch analysis from position: 0 Jump found. Position 1 = -2 filename: /Users/bps/test-php/test.php function name: (null) number of ops: 2 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > ECHO 'Hello%2C+PHP.' 3 1 > RETURN 1 branch: # 0; line: 2- 3; sop: 0; eop: 1; out1: -2 path #1: 0,
refs
- https://qiita.com/syossan27/items/056572f22236c971669a
- https://qiita.com/7968/items/06b8394fb879978258f6
JVM Specification
JVM Specificatonについて言及されていました。JVMの気持ちになった経験がなかったので、少し読みに行ってみます。
Chapter 1 Introductionの歴史の話がエモい気持ちになってよかったです(小並感のある感想)。
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-1.html#jvms-1.1
感想まとめ
「PHPわかっていなかった」という気持ちになったのでたいへん刺激になるトークでした。トーク内の実装紹介スライドにて、「constant pool countは歴史的理由で-1する」という話があって、歴史的理由を調べてみたものの見つけられなかった...。でも、そもそもこの実装の背景を理解できているかというと正直理解できていないので、精進します。