PHP 7をPHP 5の2倍高速にした方法パヌト1デヌタ構造の最適化

PHP 7.0は2015幎12月にリリヌスされたした 。 「7」に切り替えた䌁業は、生産性が向䞊し、サヌバヌの負荷が枛少したこずに気付きたした。 7぀に最初に移行したのはVebiaずEtsyで、Badoo、Avito、OLXがありたす。 Badooの堎合、7぀に切り替えるず100䞇ドルのサヌバヌ節玄になりたす。 OLXのPHP 7のおかげで、平均サヌバヌ負荷が3倍枛少し、効率が向䞊し、リ゜ヌスが節玄されたした。



Zend TechnologiesのDmitry StogovがHighLoad ++で講挔し、生産性が向䞊したした。 デコヌドPHPの内郚構造、バヌゞョン7.0の䞭心にあるアむデア、成功を決定する基本的なデヌタ構造ずアルゎリズムの倉曎に぀いお。



免責事項2019幎3月珟圚、 サむトの80は PHPで実行され 、サむトの 70は PHP 5で 実行されおい たすが、2019幎1月1日以降、 このバヌゞョンはサポヌトされおいたせん 。 PHP 5ず7の間で生産性が2倍に跳ね䞊がったための原則に関する2016幎のDmitryのレポヌトは、2019幎3月にも関連しおいたす。



スピヌカヌに぀いおDmitry Stogovは80幎代にプログラミングを開始したした。「Electronics B3-34」、Basic、アセンブラヌです。 2002幎にDmitryはPHPに粟通し、すぐに改善に取り組み始めたした。圌はPHP甹Turck MMCacheを開発し、PHPNGプロゞェクトを䞻導し、PHP甹JITの䜜業で重芁な圹割を果たしたした。 過去14幎間、Zend Technologiesのプリンシパル゚ンゞニア。



Zend Technologiesは、PHPずそれに基づいた商甚゜リュヌションを開発しおいたす。 1999幎に、むスラ゚ルのプログラマヌであるAndy GutmansずZeev Suraskiによっお蚭立されたした。2幎前にPHP 3を䜜成したした。



Zend Technologiesは、そのためにPHPコアずアプリケヌションを開発したす。䜜業䞭、拡匵機胜を䜜成し、すべおのサブシステムに入り、時にはPHPに接続されおいない商甚プロゞェクトに参加する必芁がありたした。 しかし、私にずっお最も興味深いトピックは垞にパフォヌマンスです。



Zendに参加する前からPHPを高速化する方法を探し始め、䌚瀟ず競合する自分のプロゞェクトに取り組みたした。 プロゞェクトの䜜業䞭に、私は蚀語を完党に理解し、䞻流プロゞェクトではなく、スクリプト実行の特定の偎面のみに圱響を䞎えるこずができ、すべおの最も興味深く効果的なものはカヌネルでのみ䜜成できるこずに気付きたした 。 この理解ず偶然が私をZendに導きたした。



PHPの歎史ぞの小さな䜙談



PHPは単なるプログラミング蚀語ではありたせん 。 PHPはPersonal Home Pageの略で、個人のWebペヌゞず動的なWebサむトを䜜成するためのツヌルです。 蚀語はその䞻芁郚分の1぀にすぎたせん。 PHPは巚倧な関数ラむブラリであり、デヌタベヌスやXMLパヌサヌぞのアクセスなど、他のサヌドパヌティラむブラリず連携するための倚くの拡匵機胜であり、さたざたなWebサヌバヌず通信するためのモゞュヌルセットです。



デンマヌクのプログラマヌ、 ラスマス・ラヌドルフは1995幎6月に PHP を導入したした 。 圓時、それはPerlで曞かれたCGIスクリプトの単なるコレクションでした 。 96幎4月、ラスマスはPHP / FIを導入し、6月にはPHP / FI 2.0がリリヌスされたした。 その埌、このバヌゞョンはAndy GutmansずZeev Suraskyによっお倧幅に改蚂され、98回目にPHP 3.0がリリヌスされたした。 2000幎たでに、蚀語は、蚀語ず内郚アヌキテクチャの䞡方の芳点で今日芋られるような皮類になりたした-Zend Engineに基づくPHP 4。



バヌゞョン4以降、PHPは進化しおいたす。 タヌニングポむントは、 オブゞェクトモデルが完党に曎新された2004幎のPHP 5のリリヌスでした。 PHPフレヌムワヌクの時代を開き、パフォヌマンスの問題を新たなレベルに匕き䞊げたのは圌女でした。 これを予想しお、5.0のリリヌス盎埌にZendでPHPの高速化を怜蚎し、生産性の向䞊に取り組み始めたした。



合成テストで2016幎11月にリリヌスされたバヌゞョン7.1 は、2002バヌゞョンよりも25倍高速です 。 さたざたなブランチでのパフォヌマンスの倉化のグラフによるず、䞻なブレヌクスルヌは5.1および7.0で芋られたす。







バヌゞョン5.1では、パフォヌマンスの䜜業を始めたばかりで、すべおを匕き受けたした-刀明したしたが、5.3の埌、壁にぶ぀かり、むンタヌプリタヌを改善しようずする詊みはすべおなくなりたした。



それにもかかわらず、どこを掘るかを芋぀けお、予想よりさらに倚くを取埗したした-テストでの以前のバヌゞョン5.6ず比范しお2.5倍の加速。 しかし、最も興味深いのは、䞍倉の実際のアプリケヌションで同じ2.5倍の加速が埗られたこずです。 これは珟象です。これは、以前のファクタヌ2が5幎間の10幎間にわたっお開発されたためです。







合成テストでの5.1の倧きなゞャンプは、実際のアプリケヌションでは目立ちたせん。 その理由は、䜿甚方法が異なるず、PHPのパフォヌマンスは異なるサブシステムに関連するブレヌキにかかっおいるためです。



PHP 7の歎史は 、2012幎に始たり、7番目のバヌゞョンのリリヌスで2015幎に終了した3幎間の停滞から始たりたす。 それから、通蚳を少し改良しおも生産性を䞊げるこずができないこずに気付き、JIT偎に向かいたした。



JITをさたよう



PHP-5.5のJITプロトタむプに玄2幎を費やしたした。 最初に、非垞に単玔なコヌドを生成したした-暙準ハンドラヌの呌び出しシヌケンス、ステッチされたFortコヌドのようなもの。 その埌、圌らは独自のランタむムアセンブラヌ 、回避策のためのむンラむンの別個のコヌドを曞きたしたが、そのような䜎レベルの最適化はテストでも実甚的な効果を䞎えないこずに気付きたした。



次に、静的解析メ゜ッドを䜿甚しお倉数タむプを導出するこずを考えたした。 結論に気付いた埌、 テストで2倍の加速をすぐに受けたした。 奚励されお、圌らはグロヌバルレゞスタアロケヌタを曞き蟌もうずしたしたが、倱敗したした。 かなり高レベルの衚珟を䜿甚したしたが、レゞスタの割り圓おに䜿甚するこずはほずんど䞍可胜でした。



䜎レベルの問題を回避するために、LLVMを詊しおみるこずにし、1幎埌にbench.phpの10倍の加速を埗たしたが、実際のアプリケヌションでは䜕もしたせんでした。 さらに、実際のアプリケヌションのコンパむルには数分かかるようになりたした。たずえば、 Wordpressぞの最初の芁求には2分かかり、高速化は行われたせんでした。 もちろん、これは実際の緎習にはたったく䞍適切でした。



適切な型予枬を行うず、適切なコヌドが可胜になりたすが、実際のアプリケヌションではうたく機胜せず、PHPデヌタ構造を䜿甚するず、生成されたコヌドが非効率になりたす。


䜕が遅くなりたすか



倱敗の理由を再考し、PHPが遅い理由をもう䞀床確認するこずにしたした。 この写真は、Wordpressホヌムペヌゞぞのいく぀かのリク゚ストのプロファむリングの結果を瀺しおいたす。







バむトコヌドの解釈に費やされるのは30未満、メモリマネヌゞャヌのオヌバヌヘッドは20、ハッシュテヌブルの凊理は13、正芏衚珟の凊理は5です。



JITで働いお、最初の30だけを取り陀き、他のすべおは無駄になりたした。 ほがすべおの堎所で、メモリ割り圓お、参照カりントなどのオヌバヌヘッドを䌎う暙準のPHPデヌタ構造を䜿甚せざるを埗たせんでした。 この理解により、PHPの䞻芁なデヌタ構造を眮き換える必芁があるずいう結論に至りたした。 この基盀の眮き換えにより、 PHPNGプロゞェクトが始たりたした。



Phpng 新䞖代



このプロゞェクトは、PHP甚のJITを䜜成しようずしお倱敗した埌に開発されたした。 䞻な目暙は、新しいレベルの生産性を達成し、将来の改善の基盀を築くこずです。



しばらくの間、パフォヌマンスの枬定に合成テストを䜿甚しないこずを玄束したした。これらは通垞、プロセッサキャッシュに完党に収たる限られた量のデヌタを䜿甚する小さなコンピュヌティングプログラムです。 察照的に、実際のアプリケヌションはサブシステムメモリに関連するブレヌキの圱響を受け、メモリからの1回の読み取りには100の蚈算呜什がかかりたす。 PHPNGプロゞェクトは、メモリアクセスを最適化するための䞻芁なPHPデヌタ構造のリファクタリングです 。 むノベヌションなし、100PHP 5互換。



これらの構造を倉曎する方法は明確でした。 しかし、 PHP自䜓のコアは150,000行であり、ほが3分の1を倉曎する必芁があるため、䟝存する倉曎の量は膚倧でした。 基本ディストリビュヌションに含たれる100以䞊の拡匵機胜、さたざたなWebサヌバヌ甚の1ダヌスのモゞュヌルを远加するず、プロゞェクトの壮倧さを実感できたす。



プロゞェクトを終了するかどうかさえ確信できたせんでした。 そのため、圌らは秘密裏にプロゞェクトを立ち䞊げ、最初の楜芳的な結果が珟れたずきにのみプロゞェクトを開きたした。 単にカヌネルをコンパむルするのに2週間かかりたした。 2週間埌、bench.phpが獲埗したした。 Wordpressの動䜜を保蚌するために1か月半を費やしたした。 1か月埌、プロゞェクトを開きたした-2014幎5月でした。 圓時、 Wordpressの加速は30でした。 それはすでに壮倧なむベントのように思えたした。



PHPNGはすぐに関心を集め、2014幎8月にPHP 7の将来の基盀ずしお採甚されたした 。 これは異なるプロゞェクトであり、異なる目暙セットを持ち、生産性はそのうちの1぀にすぎたせんでした。



PHP 7.0



バヌゞョン番号7自䜓は疑わしいものでした。 前のバヌゞョンは5番目でした。 6぀目は数幎前に開発され、完党にネむティブUnicodeサポヌトに専念しおいたしたが、開発の初期段階で行われた決定の倱敗により、カヌネルコヌドず各拡匵機胜が過床に耇雑になりたした。 最終的に、プロゞェクトを凍結するこずが決定されたした。



このずきたでに、PHP 6に捧げられた倚くの資料がすでに蓄積されおいたした。䌚議でのスピヌチ、出版された本です。 誰も混乱させないために、PHP 6をスキップしおPHP 7プロゞェクトを呌び出したした。このバヌゞョンは幞運でした-PHP 7はほが蚈画どおり2015幎12月にリリヌスされたした。



パフォヌマンスに加えお、PHP 7には長い間埅ち望たれおいたむノベヌションがいく぀か登堎したした。





むノベヌションは良いが、内郚の倉化に戻る。 PHP 7がたどった道ず、この道が私たちを導く堎所に぀いお話したしょう。



zval



これは、PHPの基本的なデヌタ構造です。 PHPの倀を衚すために䜿甚されたす 。 蚀語は動的に型付けされ、倉数の型はプログラムの実行䞭に倉曎できるため、IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_ARRAY、IS_OBJECTなどの倀をずるこずができる型フィヌルドzend_uchar型を栌玍する必芁がありたす。ナニオン倀で衚される倀。敎数、実数、文字列、配列、たたはオブゞェクトを栌玍できたす。



PHP 5のzval



このような各構造のメモリは、ヒヌプで個別に割り圓おられたした。 タむプず倀に加えお、構造ぞの参照のカりンタヌもその䞭に保存されたした。 そのため、構造䜓はメモリマネヌゞャヌのオヌバヌヘッドずそのポむンタヌをカりントせずに24バむトを䜿甚したした。



右䞊の写真は、単玔なスクリプト甚にPHP 5のメモリに䜜成されたデヌタ構造を瀺しおいたす。







スタックでは、ポむンタヌで衚される4぀の倉数にメモリが割り圓おられたした。 倀自䜓zvalはヒヌプ䞊にありたす。 この䟋では、これらは2぀のzvalであり、それぞれが2぀の倉数によっお参照されるため、参照カりンタヌは2に蚭定されたす。



型たたはスカラヌ倀にアクセスするには、少なくずも2぀の読み取り倀が必芁です。最初にポむンタヌの倀を読み取り、次に構造䜓の倀を読み取りたす。 スカラヌ倀ではなく、たずえば文字列たたは配列の䞀郚を読み取る必芁がある堎合は、少なくずももう1回読み取る必芁がありたす。



PHP 7のzval



以前にポむンタヌを䜿甚しおいた堎所で、7぀にzvalを埋め蟌み始めたした。 スカラヌ型の参照カりントから離れたした。 フィヌルドのタむプず倀には倧きな倉曎はありたせんでしたが、いく぀かのフラグず予玄枈みの堎所が远加されたした。これに぀いおは埌で説明したす。







巊偎はPHP 5での倖芳、右偎はPHP 7での倖芳です。







珟圚、zval自䜓がスタック䞊にありたす。 型ずスカラヌ倀を読み取るには、1぀の機械語呜什で十分です。 すべおの倀は1぀のメモリ領域にグルヌプ化されたす。぀たり、ロヌカル倉数を操䜜する堎合、プロセッサキャッシュのミスによる損倱は実質的にありたせん。 ただし、コピヌが必芁な堎合は、新しいパフォヌマンスの真の力が含たれたす。



レコヌドをコピヌ



スクリプトの最䞊行に、別の割り圓おが远加されたした。







PHP5では、新しいzvalのヒヌプからメモリを割り圓お、そのint2を初期化し、倉数bぞのポむンタヌの倀を倉曎し、bが先に参照した倀の参照カりンタヌを枛らしたした。



PHP 7では、いく぀かの呜什を䜿甚しお倉数bを盎接所定の䜍眮に初期化したしたが、PHP 5では数癟の呜什が必芁でした。 したがっお、zvalはメモリ内で衚瀺されたす。







これらは2぀の64ビットワヌドです。 最初の単語の意味は、敎数、実数、たたはポむンタヌです。 2番目の単語には、 タむプ 意味の解釈方法を瀺す、フラグ、および䜍眮合わせ䞭に远加される予玄堎所が含たれたす。 しかし、消えるこずはありたせんが、間接的に関連する倀を保存するために異なるサブシステムによっお䜿甚されたす。



フラグはビットのセットで 、各ビットはzvalがプロトコルをサポヌトしおいるかどうかを瀺したす。 たずえば、 IS_TYPE_REFCOUNTED



堎合、このzvalを操䜜するずき、゚ンゞンは参照カりンタヌの倀を凊理する必芁がありたす。 割り圓おるずきは増加し、スコヌプを出るずきは枛少し、参照カりンタヌがれロに達するず、䟝存構造を砎壊したす。



タむプのうち、PHP 5ず比范しお、いく぀かの新しいタむプが登堎したした。





IS_UNDEF



からIS_UNDEF



たでのIS_DOUBLE



はスカラヌであり、远加のメモリを必芁ずしたせん。 それらをコピヌするには、最初のマシン64ビットワヌドを倀でコピヌし、2番目の半分をタむプずフラグでコピヌしたす。



カりントダりン



他のタむプではより困難です。 それらはすべお䞋䜍構造によっお衚され、zvalはこの構造ぞの参照を単に栌玍したす。 タむプごずにこの構造は異なりたすが、OOPの芳点からは、すべお共通の抜象祖先たたは構造zend_refcountedがありたす。 ガベヌゞコレクタヌの参照カりントおよびその他の情報が栌玍される最初の64ビットワヌドの圢匏を決定したす。







この単語は単にガベヌゞコレクタヌの情報ず芋なすこずができ、特定のタむプの構造䜓はこの最初の単語の埌にフィヌルドを远加したす。



行



文字列の7぀には、ハッシュ関数の蚈算倀、その長さ、および文字自䜓が栌玍されたす。 このような構造のサむズは可倉であり、文字列の長さに䟝存したす。 必芁に応じお、文字列に察しおハッシュ関数が1回蚈算されたす。 PHP 5では、あらゆるニヌズに応じお再蚈算されたした。







文字列は参照カりント可胜になりたした。PHP5で文字自䜓をコピヌした堎合、この構造の参照カりントを増やすだけで十分です。



PHP 5のように、 䞍倉たたはむンタヌンされた文字列の抂念がただありたす 。 通垞、これらは1぀のむンスタンスに存圚し、ク゚リの最埌たで存続し、スカラヌ倀のように動䜜できたす。 それらぞのリンクのカりンタヌを管理する必芁はありたせん。コピヌするには、4぀のマシン呜什を䜿甚しおzvalのみをコピヌするだけで十分です。



配列



配列は組み蟌みのハッシュテヌブルで衚され、PHP 5ずそれほど違いはありたせん。ハッシュテヌブル自䜓は倉曎されたしたが、個別に倉曎されおいたす。







配列は、保存されたデヌタに応じお内郚構造ず動䜜をわずかに倉曎する適応構造になりたした。 近い数倀キヌを持぀芁玠のみを保存する堎合、Cの配列の速床に匹敵する速床でむンデックスによっお盎接芁玠にアクセスできたす。ただし、同じ配列に文字列キヌを持぀芁玠を远加するず、衝突解決を䌎う実際のハッシュになりたす。



これは、ハッシュテヌブルがPHP 5でどのように芋えるかです。







これは、線圢リスト右䞊隅に衚瀺を䜿甚した衝突解決を備えた叀兞的なハッシュテヌブルの実装です。 各アむテムはバケットで衚されたす。 すべおのバケットは、衝突を解決するために二重にリンクされたリストによっおリンクされ、順番に反埩する別の二重にリンクされたリストによっおリンクされたす。 各zvalの倀は個別に割り圓おられたす-Bucketでは、そのリンクのみを保存したす。 たた、文字列キヌは個別に割り圓おるこずができたす。



したがっお、ハッシュテヌブルごずに、倚くの小さなメモリブロックを割り圓おる必芁があり、埌で䜕かを芋぀けるには、ポむンタヌに沿っお実行する必芁がありたす。 このような遷移はそれぞれ、キャッシュミスず10〜100プロセッササむクルの遅延を匕き起こす可胜性がありたす。



これがPHP 7で発生したこずです。







論理構造は倉曎されず、物理構造のみが倉曎されたした。 珟圚、ハッシュテヌブルの䞋で、メモリは1぀の操䜜で割り圓おられたす。



この図では、ベヌスポむンタヌの䞋郚に芁玠があり、䞊郚にハッシュ関数でアドレス指定されるハッシュ配列がありたす。 フラット配列たたはパック配列の堎合、数倀むンデックスを持぀芁玠のみを栌玍するず、䞊郚はたったく割り圓おられず、バケットを番号で盎接アドレス指定したす。



芁玠をバむパスするには、それらを䞊から䞋、たたは䞋から䞊に順番に䞊べたす。これは、最新のプロセッサヌで問題なく行われたす。 倀はバケットに組み蟌たれたすが、それらの予玄スペヌスは衝突を解決するためにのみ䜿甚されたす。 同じハッシュ関数倀たたはリストマヌカヌの最埌に別のバケットのむンデックスを保存したす。



キヌの文字列倀のメモリは個別に割り圓おられたすが、それでも同じzend_stringです。 配列に貌り付けるずきは、文字列の参照カりンタヌを増やすだけで十分です。以前は文字を盎接コピヌする必芁がありたしたが、怜玢時には文字ではなく文字列自䜓ぞのポむンタヌを比范できるようになりたした。



䞍倉配列



以前は、䞍倉の文字列がありたしたが、䞍倉の配列も登堎したした。 文字列ず同様に、それらは参照カりンタヌを䜿甚せず、リク゚ストが終了するたで砎棄されたせん。 これは、100䞇個の芁玠の配列を䜜成する単玔なスクリプトで、各芁玠は単䞀の「hello」芁玠を持぀同じ配列です。







PHP 5では、ルヌプの各反埩で、新しい空の配列が䜜成され、「hello」が曞き蟌たれ、すべおが結果の配列に远加されたした。 PHP 7では、コンパむル時に、スカラヌのように動䜜する䞍倉配列を1぀だけ䜜成し、結果の配列に远加したす。 提瀺された䟋では、これにより、メモリ消費量が10倍以䞊、加速がほが10倍以䞊になりたす。



もちろん、実際のアプリケヌションでは数癟䞇の芁玠の定数配列はあたり芋られたせんが、小さなものは非垞に䞀般的です。 それらのそれぞれで、あなたは小さいが、勝利を埗るでしょう。



オブゞェクト



PHP 5のすべおのオブゞェクトぞのリンクは別のリポゞトリにあり、zvalにはハンドルのみがありたした-䞀意のオブゞェクトID。







オブゞェクトに到達するために、少なくずも3぀の読み取りを行いたした。 さらに、オブゞェクトの各プロパティの倀のメモリは個別に割り圓おられ、それを読み取るには少なくずも2぀以䞊の読み取り倀が必芁でした。



PHP 7では、盎接アドレス指定に移行できたした。







zend_object



アドレスは、単䞀のマシン呜什でアクセスできるようになりたした。 たた、プロパティはビルトむンされおおり、それらを読み取るには、远加の読み取りが1぀だけ必芁です。 たた、グルヌプ化されおいるため、 デヌタの局所性が向䞊し、最新のプロセッサが぀たずかないようになりたす。



事前定矩されたプロパティに加えお、このオブゞェクトのクラスぞのリンク、いく぀かのハンドラヌ仮想メ゜ッドテヌブルの類䌌物、および定矩されおいないプロパティのハッシュテヌブルもここに栌玍されたす。 PHPでは、元々定矩されおいなかったオブゞェクトにプロパティを远加できたす。いく぀かのマシン呜什で定矩枈みのプロパティにアクセスするのに十分な堎合は、定矩枈みでないプロパティの堎合、ハッシュテヌブルを䜿甚する必芁がありたす。 もちろん、これははるかに高䟡です。



参照先



最埌に、PHPリンクを衚す別のタむプを導入する必芁がありたした。







これは完党に透明なタむプです。 PHPスクリプトには衚瀺されたせん。 スクリプトは、zend_reference構造に組み蟌たれた別のzvalを参照したす。 このような構造を少なくずも2箇所から参照し、この構造の参照カりンタヌは垞に1より倧きいこずが理解されたす。カりンタヌが1に䞋がるず、リンクは通垞のスカラヌ倀に倉わりたす。 リンクに埋め蟌たれたzvalは、それを参照する最埌のzvalにコピヌされ、構造自䜓が削陀されたす。



珟圚、参照での䜜業は他の型よりもはるかに耇雑であるようですそしお、これは事実ですが、PHP 5では、実際には、任意の倀玠数敎数でさえにアクセスする際に同等の耇雑さの䜜業を行う必芁がありたした。 珟圚、より耇雑なプロトコルを1぀のタむプのみに適甚しおいるため、他のすべおの、特にスカラヌ倀での䜜業が高速化されおいたす。



IS_FALSEおよびIS_TRUE



単䞀のタむプIS_BOOLが別々のIS_FALSEずIS_TRUEに分割されたこずは既に述べたした。 このアむデアはLuaJITの実装でスパむされ、最も䞀般的な操䜜の1぀である条件付き移行を高速化するために䜜成されたした。







PHP 5でタむプを読み取り、ブヌル倀をチェックし、倀を読み取り、trueたたはfalseであるかどうかを確認し、これに基づいお遷移を行う必芁があった堎合、タむプをチェックしおtrueず比范するだけで十分です。





呌び出し芏玄



呌び出し芏玄たたは関数呌び出し芏玄の倉曎は、デヌタ構造だけでなく基瀎ずなるアルゎリズムにも圱響する重芁な最適化です。 巊偎の図には、foo関数ずその呌び出しで構成される小さなスクリプトがありたす。 以䞋は、このスクリプトがPHP 5でコンパむルされたバむトコヌドです。







たず、PHP 5でどのように機胜したかを説明したす。



PHP 5の呌び出し芏玄



最初のSEND_VAL



ステヌトメントは、倀「3」をfoo関数に送信するこずでした。 これを行うために、圌女はヒヌプに新しいzvalを割り圓お、そこに倀3をコピヌし、この構造䜓ぞのポむンタヌの倀をスタックに曞き蟌むこずを䜙儀なくされたした。







2番目の呜什でも同様です。 さらにDO_FCALL



CALL FRAME



初期化し、ロヌカル倉数および䞀時倉数の堎所を予玄し、呌び出された関数に制埡を移したした。







最初のRECV



は最初の匕数をチェックし、察応するロヌカル倉数$ aでスタック䞊のスロットを初期化したした。 ここでは、コピヌせずに、察応するパラメヌタヌの参照カりンタヌを増やしたした倀3のzval。 同様に、2番目のRECV



は、倉数$ bずパラメヌタヌ5の間に接続RECV



確立したした。







さらなる身䜓機胜。 3 + 5の远加が発生したした-刀明したした8.これは䞀時倉数であり、その倀はスタックに盎接栌玍されおいたした。







戻り、関数から戻りたす。







戻った時点で、スコヌプ倖のすべおの倉数ず匕数を解攟したす。 これを行うには、解攟されたフレヌムのスロットが参照するすべおのzvalを調べ、それぞれに぀いお参照カりントを枛らしたす。 0に達するず、察応する構造を砎壊したす。



ご芧のように、関数に定数を送信するような単玔な操䜜でも、新しいメモリを割り圓お、参照カりンタをコピヌしお増加させ、その埌、二重に枛少させお削陀する必芁がありたす。



PHP 7の呌び出し芏玄



PHP 7では、これらの問題を修正したした。珟圚、スタックにはzvalポむンタヌではなく、zvalポむンタヌ自䜓を栌玍しおいたす。







たた、新しい呜什INIT_FCALL



導入したした。この呜什は、 CALL FRAME



䞋でメモリの初期化ず割り圓おを行い、匕数ず䞀時倉数甚のスペヌスを予玄したす。







SEND_VAL 3



は、匕数をCALL FRAME



埌の最初のスロットにコピヌするだけです。 次のSEND_VAL 5



を2番目のスロットに。







その埌、最も興味深い。 DO_FCALL



は、呌び出された関数の最初の呜什に制埡を枡すように思われたす。 しかし、匕数は既に倉数パラメヌタヌ$ aおよび$ bに予玄されおいるスロットにヒットしおおり、 RECV



呜什は䜕もしたせん。 したがっお、単玔にスキップできたす。 2぀のパラメヌタヌを送信したため、2぀の指瀺をスキップしたす。 圌らが3぀を送ったならば、圌らは3぀を逃したでしょう。







したがっお、関数の本䜓に盎接移動し、远加しお戻りたす。







戻るずき、すべおのロヌカル倉数をクリアしたすが、珟圚は2぀のスロットのみで、そこにスカラヌがあるので、再び䜕もする必芁はありたせん。







私の話は少し単玔化されおいたすが、可倉数の匕数を持぀関数や、型チェックやその他のポむントの必芁性は考慮されおいたせん。



新しいCalling Conventionは、互換性を少し壊しおいたす 。 PHPにはfunc_get_arg



やfunc_get_args



などの関数がありfunc_get_args



。 以前に送信されたパラメヌタヌの元の倀を返した堎合は、元の倀を保存しないため、察応するロヌカル倉数の珟圚の倀を返したす。 C.デバッガヌが行うように







さらに、関数に同じ名前の耇数のパラメヌタヌを含めるこずはできなくなりたした。 これには意味がありたせんでしたが、このようなPHPコヌドfoo($_, $_)



に出䌚いたした。 どのように芋えたすか プロロヌグを認識したした



新しいメモリマネヌゞャヌ



デヌタ構造ず基本的なアルゎリズムの最適化を終えお、すべおのブレヌキサブシステムに再び泚目したした。 PHP 5のメモリマネヌゞャヌは、Wordpressのプロセッサ時間のほが20を占めおいたした。



倧量の割り圓おを取り陀いた埌、圌のオヌバヌヘッドコストは䜎くなりたしたが、それでもかなりの額になりたした。これは、かなりの仕事をしおいたからではなく、キャッシュに぀たずいたからです。 これは、埓来のDoug Leaのmallocアルゎリズムを䜿甚したために発生したした。これは、リンクずツリヌを移動するこずで適切な空きメモリ領域を芋぀けるこずを意味し、これらのすべおのトリップは必然的にキャッシュミスを匕き起こしたした。



今日、最新のプロセッサの機胜を考慮した新しいメモリ管理アルゎリズムがありたす。 たずえば、Googleの jemallocずptmallocです 。 最初は、それらを倉曎せずに䜿甚しようずしたしたが、PHP固有の機胜がないため、リク゚ストの最埌にメモリを完党に解攟するのに費甚がかかるため、勝ちたせんでした。 その結果、dlmallocを攟棄し、叀いメモリマネヌゞャずjemallocのアむデアを組み合わせお、独自の䜕かを䜜成したした。



Memory Managerのオヌバヌヘッドを5に削枛し、サヌビス情報のメモリオヌバヌヘッドを削枛し、CPUキャッシュの䜿甚を改善したした。 適切なメモリブロックがビットマップで怜玢されるようになり、小さなブロックのメモリは個別のペヌゞから割り圓おられ、リリヌス時にキャッシュされ、頻繁に䜿甚されるブロックサむズに特化した機胜が远加されたす。



倚くの小さな改善



最も重芁な改善点に぀いおのみ説明したしたが、もっず小さな改善点がありたした。 それらのいく぀かに蚀及するこずができたす。





いく぀かは非垞に単玔でした。たずえば、通垞の真珠衚珟でJITを有効にするのに3行のコヌドしか必芁ずしなかったため、ほずんどすべおのアプリケヌションですぐに目に芋える2-3加速がもたらされたした。 その他の最適化は、特定のPHP関数のいく぀かの狭い偎面に觊れおおり、特に興味深いものではありたせんが、これらの小さな改善すべおの党䜓的な貢献は非垞に重芁です。



䜕に来たしたか



これは、WordPress / PHP 7.0のさたざたなサブシステムの貢献です。







仮想マシンの貢献床は50に増加したした。Memory Managerの消費量はすでに5未満です。これは、䞻にMemory Manager自䜓の最適化によるものではなく、呌び出し回数を枛らしたためです。同じテストで以前にメモリが1億3,000䞇回割り圓おられた堎合、珟圚は1,000䞇回です。すべおの䞻な加速は、メモリマネヌゞャのオヌバヌヘッドを枛らし、デヌタ構造の改善によりメモリマネヌゞャぞの呌び出し回数を枛らすこずで達成されたように芋えたすが、実際にはすべおのサブシステムが倧幅に改善されたした。





加速の䞻な源





2,5- , . なぜそう , , CPU time, — , . PHP , .



PHP 7



WordPress 3.6 — . - , PHP 7 mysql, , .







, PHPNG. 2/3 . , .



, WordPress, , — 1,5 2- .



PHP 7 HHVM



HHVM.







— . . Facebook . HHVM . , , , , .







PHP 7 — . Vebia, Etsy Badoo. Highload- , .



PHP 7.0 Etsy Badoo -. Badoo .







, 2 , — 7 .



PHP 7.0. , PHP 7.1, .



PHP Russia PHP 8 . PHP, , , — 1 . , , — , , , .




All Articles