Software engineering from east direction

六本木一丁目で働くソフトウェアエンジニアのブログ

PHPerKaigi 2019 の「PHPでJVMに入門する」を聞いて

TL;DR

  • PHPerKaigiでのめもりーさんの「PHPJVMに入門する」がおもしろかった
  • 自分の知らなかったことを調べて残しておく

トークPHPJVMに入門する」

fortee.jp

@m3m0r7さんのPHPJVMに入門するという内容がおもしろかった。PHPJavaを動かすという力強いトーク

自分のトーク時間がちょうど裏で当日現場にて聞けなったので、YouTubeで公開されている動画を拝聴しました。

www.youtube.com

PHPバイトコードを読む方法など、いろいろPHPのことを知らなかったので、今回知らなかったことを調べてみます。

個人的な調べごと

PHPのfloat・doubleについて

トーク内で、PHPのfloat・doubleについて一瞬だけ触れていらっしゃたのあらためて調べてみる。

とりあえず、PHPの公式ドキュメント「浮動小数点数」をこちらに。

www.php.net

このページには、「浮動小数点数の精度」という警告があるんですね。

浮動小数点数の精度は有限です。 システムに依存しますが、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

バイナリリーディング

トーク中のスライドでバイナリを表示する際のコマンドに xxd を使っていて、「それなんだっけ」って思ったので基礎的なことだろうなと自分を恥じつつ調べます。

www.tutorialspoint.com

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

vld extension

PHPJavaを読むことで、PHPの中間コードが理解しやすくなるという話がありました。vld extensionの気持ちになれるということなんですが、「なるほど、vld extensionな(知らない)」という自分でしたので調べます。Vulcan Logic Disassemblerの略でvldとのことで、PECLで配布されています。

pecl.php.net

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

JVM Specification

JVM Specificatonについて言及されていました。JVMの気持ちになった経験がなかったので、少し読みに行ってみます。

docs.oracle.com

Chapter 1 Introductionの歴史の話がエモい気持ちになってよかったです(小並感のある感想)。

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-1.html#jvms-1.1

感想まとめ

PHPわかっていなかった」という気持ちになったのでたいへん刺激になるトークでした。トーク内の実装紹介スライドにて、「constant pool countは歴史的理由で-1する」という話があって、歴史的理由を調べてみたものの見つけられなかった...。でも、そもそもこの実装の背景を理解できているかというと正直理解できていないので、精進します。