PHPerと揶揄されないようにPHPコアとオペコードに触れてみる

一般的には「初学者でも学びやすい!?」と言われるPHP言語。

私も4年近く触っていますが、PHPは何かとdisられがちです(;^_^

TaNA
敷居が低い分、意味も知らずにコピペを乱用するプログラマーが多いらしく、故にPHPしか出来ない人をPHPer(ペチパー)と揶揄する風潮です。

PHPの問題ではなく、PHPを使う低レベルプログラマーに問題があるんでしょうけどね。

私もPHPerと揶揄されないよう、この本を参考にPHPコアとオペコードに触れてみました。

PHP基礎を学べる参考書

ちなみにPHPコア以前に、PHP言語の基礎がまだの方はこちらの参考書がオススメです。

私は初めて触れた言語がJavaでしたが、PHPを触る時はこの本を多用していました。

言語仕様からクラスの使い方、PHPの歴史について網羅的に知れる一冊です。

TaNA
今となってはちょっとバージョンが古いですが(;^_^

本書の中でもPHPコアに関わる話が少しだけ書かれていました。

PHPではコンパイルを行うことなくプログラムを実行できますが、実際にはPHPプログラムが実行される度、内部的にソースコードのコンパイルを行います。

PHPプログラムが実行されると、内部でオペコード(Opcodes:Operationcodes)と呼ばれる中間コードにコンパイルを行い、そのオペコードを実行マシンが実行します。

あくまで触りの部分だけですが。

PHPコアのZend Engine

PHPコアのエンジンとなっているのが「Zend Engine」。

Zend Engineはスクリプトを最小単位の命令である「オペコード」に変換してから実行。スクリプトを処理してオペコードへとコンパイルするためには、字句解析と構文解析を行います。

TaNA
ちなみに字句解析はコメントアウトされたソースもトークンとして切り出されるので、パフォーマンスを考慮するのであれば、不要なロジックはコメントアウトではなく、削除が好ましいようですね。

オペコード目線でパフォーマンスがどう変わるのか、いくつか例を挙げています。

特に気になったものを抜粋(オペコードは省略)

1.forの最大値が変数 vs 関数

2.条件分岐 if-else vs 三項演算子

3.比較演算子 == vs ===

4.print() vs echo()

1は前者の最大値が変数の方が圧倒的に速い。

TaNA
オペコードは似ていますが、関数呼び出しのオーバーヘッドが大きいようです。

2は三項演算子が複雑でも処理速度はほぼ同じになるようです。

TaNA
若干前者が速いので、可読性を高める意味で三項演算子はアリ。

3は言うまでもなく「===」が速い。

TaNA
「==」は色んな型に変換しようと試みるので、当然遅くなるみたいですね。バグの温床にもなり得るので、余程理由が無ければ「===」一択ですね。

最後の4について、あんま意識してませんでしたが。

TaNA
print()は内部でecho()を呼び出しているらしく、若干echo()の方が速い模様。

似たような処理でも、内部のオペコードは結構違っていることに気付けます。

zvalのコピーオンライト

Zend EngineはC言語で実装されています。

TaNA
内部構造を知るには、構造体やポインタを理解する必要があるので、C言語の基礎知識があると読みやすいかも。私は知らないので、全ては理解出来ませんでした。

POINTPHPコアは変数を最適化する「コピーオンライト」の仕組みが備わっている。

変数をコピーしただけでは同じメモリ領域を参照するだけで、コピーした変数の値を書き換えた時に初めて変数のコピーが行われる仕組み。

TaNA
上の例では同じ領域を参照しているので、参照カウント(refcount__gc)が2となりますが、値が書き込まれるタイミングでzvalがコピーされ、分離される。

PHP7から変数・浮動小数店・真偽値はコピーオンライトはやめ、即コピーになった模様。

オブジェクト指向は遅い!?

PHPの配列やオブジェクトのプロパティ・メソッドはHasgTableに登録して管理されますが、以下の観点に触れながら、なぜオブジェクト指向が遅いのか説明されていました。

・クラスの宣言

・クラスの継承

・プロパティへのアクセス

・メソッド呼び出し

TaNA
ただクラスの宣言・継承は、モジュール起動時に一度だけ行われるものなので、あまりパフォーマンスを気にする必要はないようです。

問題はプロパティとメソッドへのアクセス。

プロパティの場合、オブジェクトaをHashTableから呼び出し、更にプロパティの情報や値をHashTableを探し出し、そこから値を読み出すために再度HashTableから探します。

TaNA
書き込む場合も同様にHashTableへの探索回数が3倍も多く、処理時間も3倍くらいかかると考えて良いと。メソッド呼び出しも、通常の関数より遅くなります。

そうは言いつつも、昨今ではハードウェアの性能が向上しています。

保守性・開発効率・品質の向上を重視すれば、オブジェクト指向開発は欠かせません。

TaNA
まあ開発者も高性能ハードウェアに胡坐をかくのではなく、この仕組みを知り、クソコードを書かない事は意識すべきなんでしょうね。

その意識が乏しい者を、人はPHPerと揶揄するのかもしれません。